告別裸奔,趕集抓手

  • 2019 年 10 月 5 日
  • 筆記

告別裸奔,趕集抓手

1.告別裸奔

2.趕集抓手

3.作者的話

1.告別裸奔

裸奔

在爬蟲過程中,有時有些網站具有反爬蟲設置,當爬取次數到達一定程度,那麼這個網站就會禁止你的IP對其進行訪問,這就是裸奔操作,為了不讓對方伺服器發現你在爬取對面的網站資訊。

換句話說,以隱藏身份爬取對應網站,那麼這裡就採取從西刺網站爬取中國高匿代理IP設置代理參數,從而隱藏自己,接下來先來看一下,如何實現西刺ip的爬取及處理呢?

西刺代理:

http://www.xicidaili.com/nn

分析

西刺分析圖

在上圖中,三個紅色框,分別表示,ip,埠,以及類型,最終所要實現的結果是:{'HTTP':'HTTP://ip:port'}

這裡我只是利用西刺的數據,去爬取趕集網數據。所以這裡只選擇了4頁數據進行處理,如果想要更多數據,去建立一個自己的代理池,那麼只需要變動循環次數,或者獲取下一頁的url即可進行多頁面獲取!

程式碼

baseurl = 'http://www.xicidaili.com/nn/'  headers = {      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',  }  http_list = []  def get_IP():      print('-----IP爬取進度-----')      # 只爬取4頁的ip      for i in range(1,5):          print('------第' + str(i) + '頁開始爬取------')          url = baseurl + str(i)          raw_html = requests.get(url, headers=headers).text          # print(raw_html)          selector = etree.HTML(raw_html)          # td[index]中index從1開始,分別獲取ip,port,type          ip = selector.xpath('//tr[@class="odd"]//td[2]/text()')          port = selector.xpath('//tr[@class="odd"]//td[3]/text()')          httptype = selector.xpath('//tr[@class="odd"]//td[6]/text()')          # 上述ip/port/type均為list數據(存儲的是當頁數據),我們需要將其每一個數據轉換為{'HTTP':'HTTP://ip:port'}格式,用在requests.get裡面的proxies參數裡面!          for eachip,eachport,eachtype in zip(ip,port,httptype):              http_dict = {}              http_dict[eachtype] = eachtype + '://' + eachip + ':' + eachport              http_list.append(http_dict)          print(http_list) # 兩頁總共的ip          print('------第' + str(i) + '頁爬取結束------')        # 返回的數據格式為[{},{},{}.....]      return http_list  print('------IP爬蟲結束------')

數據

IP結果圖

2.趕集抓手

分析

本次主要想獲取趕集網二手房資訊,如下列欄位:

數據欄位圖

在分析中發現兩個問題:

第一:網站反爬蟲處理,當訪問次數過多,就會有驗證操作,如下圖即為當前驗證操作的源碼。

反爬蟲源碼圖

第二:我們直觀看到只有10個頁面,但是當你點擊第10個頁面(如下圖)的時候會發現,後面又有新的頁面(如下圖)了,於是這裡就不能直接通過獲取頁面總個數,進行遍歷,那麼該如何操作呢?

對於多頁面的處理除了上述,還有兩種。1.模擬js或者觸發相應事件;2.直接獲取下一頁的url,進行拼接即可。從上述方法中,我選擇了第二種,那麼這個多頁面問題就又解決了。

以下分別為打開趕集首頁以及點擊第10頁後的頁面!

前10頁圖

10頁後圖

功能

  • 西刺IP本地存儲及讀取
  • 通過西刺IP爬頁面
  • 數據提取
  • 美化列印
  • 資料庫存儲(包括mysql及mongodb)

這裡先給大家看一下,最後的運行結果,有個直觀的感受。

mongodb存儲圖

mysql存儲圖

當前為第5頁數據,有368條,前4個頁面,每頁1000條,那麼總共4368條,同上面的mongodb一致!

csv數據圖

mysql導出數據表,共4369行,減掉第一行的欄位,共4368行。

終端列印圖

實現

  • 封裝
MONGO_URI = 'localhost'  MONGO_DB = 'test' # 定義資料庫  MONGO_COLLECTION = 'ganji' # 定義資料庫表  class ganji_spider(object):      def __init__(self,mongo_uri,mongo_db):          self.client = pymongo.MongoClient(mongo_uri)          self.db = self.client[mongo_db]
  • 獲取源碼
def get_html(self, url, proxies):      headers = {          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',      }      raw_html = requests.get(url, headers=headers, proxies=proxies).text      return raw_html
  • 西刺IP存儲
def save_proxies(self, http_list):      ouf = open("valid_ip.txt", "a+")      for each_proxies in http_list:          ouf.write(str(each_proxies))          ouf.write('n')
  • 隨機行讀取上述存儲的IP(每一行代表一個proxies),利用代理爬取網頁
def avoid_verifi(self, http_list, url):      # print(http_list)      # print(len(http_list))      while True:          random_line = random.randint(1, len(http_list))          theline = open('valid_ip.txt', 'r').readlines()[random_line]          theline = theline.replace("'", '"')  # 單引號全部替換為雙引號          theline = json.loads(theline)  # str轉dict(字典)格式          print("正在使用的代理IP:" + str(theline))          try:              html = self.get_html(url, theline)              # print(html)              if "訪問過於頻繁,本次訪問做以下驗證碼校驗" not in html:                  return html          except Exception as e:              print('.....')
  • 數據提取

下面異常處理的目的是防止缺失欄位,對缺失欄位處理!

def get_AllPage(self, url):      raw_html = self.avoid_verifi(http_list, url)      selector = etree.HTML(raw_html)      try:          # 房子名稱          hose_title = selector.xpath('//dd[@class="dd-item title"]//a/text()')      except Exception as e:          hose_title = 0          print('房名error!')      try:          # 房子價格          hose_price = selector.xpath(              '//dd[@class="dd-item info"]//div[@class="price"]/span[@class="num js-price"]/text()')      except Exception as e:          hose_price = '0'          print('房價error!')      try:          # 房子單價          hose_eachprice = selector.xpath('//dd[@class="dd-item info"]//div[@class="time"]/text()')      except Exception as e:          hose_eachprice = '0'          print('單價error!')      try:          # 房子戶型          house_type = selector.xpath('//dd[@class="dd-item size"]//span[@class="first js-huxing"]/text()')      except Exception as e:          house_type = '0'          print('戶型error!')      try:          # 房子地址          span_list = selector.xpath('//dd[@class="dd-item address"]//span')          house_site = []          for each_span in span_list:              # string(.) 標籤套標籤              each_site = each_span.xpath('string(.)')              # 去掉換行及空格              each_site = each_site.replace('n', '').replace(' ', '')              house_site.append(each_site)      except Exception as e:          house_site = '0'          print('地址error!')      try:          # 房子大小          hose_size = selector.xpath('//dd[@class="dd-item size"]/span[3]/text()')      except Exception as e:          hose_size = '0'          print('大小error!')      return hose_title, hose_price, hose_eachprice, house_type, house_site, hose_size
  • 資料庫存儲

下面存儲包含兩大存儲:mongodb與mysql

def Save_DB(self, hose_title, hose_price, hose_eachprice, house_type, house_site, hose_size):      for htitle, hprice, hpr, htype, hsite, hsize in zip(hose_title, hose_price, hose_eachprice, house_type, house_site,                                                          hose_size):          data = {}          data['房子名稱'] = htitle          data['房子價格'] = hprice          data['房子單價'] = hpr          data['房子類型'] = htype          data['房子地址'] = hsite          data['房子大小'] = hsize          # mongodb存儲          self.db[MONGO_COLLECTION].insert_one(data)          self.client.close()          # mysql存儲          connection = pymysql.connect(host='localhost', user='root', password='xxxx', db='scrapydb',                                       charset='utf8mb4')          try:              with connection.cursor() as cursor:                  sql = "insert into `ganji`(`房子名稱`,`房子價格`,`房子單價`,`房子類型`,`房子地址`,`房子大小`)values(%s,%s,%s,%s,%s,%s)"                  cursor.execute(sql, (htitle, hprice, hpr, htype, hsite, hsize))              connection.commit()          finally:              connection.close()

【調用】

if __name__ == '__main__':      # 類初始化      ganji = ganji_spider(MONGO_URI,MONGO_DB)      # 調用西刺IP      http_list = get_IP()      # 存儲IP      ganji.save_proxies(http_list)      page_number = 1      index = 1      pagedict = {}      while True:          print("-----第" + str(page_number) + "頁爬取開始------")          ganji_data = {}          baseurl = 'http://cq.ganji.com'          if page_number==1:              page_link = baseurl + '/fang5/o1/'              pagedict[str(page_number)] = page_link          # 代理獲取真實的html          raw_html = ganji.avoid_verifi(http_list,pagedict[str(page_number)])          selector = etree.HTML(raw_html)            hose_title, hose_price, hose_eachprice, house_type, house_site, hose_size = ganji.get_AllPage(pagedict[str(page_number)])          # PrettyTable格式化列印          tb = pt()          tb.add_column('房子名稱',hose_title)          tb.add_column('房子價格',hose_price)          tb.add_column('房子單價',hose_eachprice)          tb.add_column('房子類型',house_type)          tb.add_column('房子地址',house_site)          tb.add_column('房子大小',hose_size)          print(tb)          t1 = time.time()          ganji.Save_DB(hose_title, hose_price, hose_eachprice, house_type, house_site, hose_size)          t = time.time() - t1          # 計算數據存儲耗時          print('存儲耗時:' + str(t))          try:              page_nextText = selector.xpath('//ul[@class="pageLink clearfix"]//li//a[@class="next"]/span/text()')[0]              print(page_nextText)          except Exception as e:              # 避免到最後一頁報錯!              break              print('爬蟲完畢!')          page_nextUrl = selector.xpath('//ul[@class="pageLink clearfix"]//li//a[@class="next"]/@href')[0]            print(page_nextUrl)          if page_nextText == '下一頁 >':              index += 1              print(index)              pagedict[str(index)] = baseurl + page_nextUrl              print(pagedict)              print("-----第" + str(page_number) + "頁爬取結束,5秒後重抓------")          else:              print("-----第" + str(page_number) + "是最後一頁,沒有下一頁,爬蟲結束---")              break            page_number += 1          print(page_number)          # time.sleep(5) 沒有代理ip,需要加上此行!        print("------爬蟲全部結束------")

3.作者的話

如果您覺得本公眾號對您有幫助,請多多轉發、交流及支援~~~謝謝!

項目地址,請點擊閱讀原文哦!覺得可以,希望給個star!