告別裸奔,趕集抓手
- 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!