Python爬蟲私活,代碼公開!采集了20000+漫展歷史數據,一言不合就開源
橡皮擦,一個逗趣的互聯網高級網蟲。
文章的起源
周末,一個群友用 1 個小時,完成一個小需求,賺了 ¥None 元。
他說:距離財富自由又近了一步,并且一度在群里不斷炫富。
然后我把它的代碼給公開了,估計他要失去這條財富之路了。
閱讀本文你將收獲
lxml 庫解析知識;
粗糙的反 反爬 技術;
XPath 語法再度了解;
20000+漫展歷史數據。
采集 20000+漫展歷史數據
目標數據分析
本次要抓取的目標為:https://www.nyato.com/manzhan/?type=expired&p=1,具體數據區域如下所示。如需要更多數據,可以在此基礎上,進一步進行擴展。
詳細數據可點擊上圖標題,進入明細頁,目標數據可參考下圖紅框區域所示內容。
列表頁與詳情頁規則如下
該網站的分頁規則比較簡單,直接修改頁碼號即可,詳情頁可以直接通過提取列表頁中的連接獲取。
https://www.nyato.com/manzhan/?type=expired&p=1 https://www.nyato.com/manzhan/?type=expired&p=2 https://www.nyato.com/manzhan/?type=expired&p=3 …… https://www.nyato.com/manzhan/?type=expired&p=1312
頁面存在反爬邏輯
在編寫代碼的過程中,發現高并發訪問目標網站,會出現短暫頁面無法加載情況,該場景常規解決方案,使用 IP 代理池。
但本文的學習目的不在反爬,顧采用最笨拙的解決方案,異常抓取。
多次測試發現網站當判定我們是爬蟲之后,會限制我們訪問頁面,但限制時間比較短,并且不會限制 IP,所以當頁面出現異常情況時,通過 try except 重新訪問頁面,中間間隔一定時間,即代碼存在如下結構:
try: # 代碼段 except Exception as e: time.sleep(4) 重新調用函數名(url) print(e)
最終運行代碼,會出現如下情況,當請求頁面出現空數據時,頁面重新發起請求,同時停留 4 秒鐘。
使用該思路爬取數據比較慢,如果你只需要數據,直接在文末下載即可。
整理需求如下
批量生成待抓取列表頁;
requests 請求目標數據;
lxml 提取目標數據;
格式化數據;
保存數據。
編碼時間
需求整理完畢之后,進入實際編碼環節,會發現本篇博客涉及的代碼比較簡單。學習重點放在 lxml 提取操作的練習。
下述代碼中的 USER_AGENTS 可以直接通過搜索引擎獲取資源,也可以自己收集整理一份。
import requests from lxml import etree from fake_useragent import UserAgent import time import re import random # USER_AGENTS USER_AGENTS = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser;", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36", ] # 保存文件 def save(long_str): try: with open(f"exhibition.csv", "a+", encoding="utf-8") as f: f.write(long_str) except Exception as e: print(e) # 獲取詳情頁數據 def get_detail(url): try: headers = {"User-Agent": random.choice(USER_AGENTS)} res = requests.get(url=url, headers=headers) text = res.text # 將 html 轉換成 Element 對象 html = etree.HTML(text) # 詳細地址 address_detail = html.xpath("http://span[@class='fl mr10']/text()")[0] # 時間間隔 time_interval = html.xpath( "http://div[@class='h25 line25 s6 f14 w100s mb10']/text()")[3].strip() # 票價 ticket_price = html.xpath("http://b[@class='f40']/text()")[0] # 匹配得分 score_url = html.xpath("http://div[@class='mt10']/img/@src")[0] score = re.search( r"http://static.nyato.cn/expo-image/stars/star-(\d\.\d).png", score_url).group(1) # 其它信息 other = ",".join(html.xpath( "http://span[@class='sf6 f18 fwb ml15']/text()")) return ticket_price, time_interval, address_detail, score, other except Exception as e: print("詳情頁BUG") print(url) time.sleep(4) get_detail(url) print(e) def run(url): try: headers = {"User-Agent": random.choice(USER_AGENTS)} res = requests.get(url=url, headers=headers) text = res.text # 將 html 轉換成 Element 對象 html = etree.HTML(text) # xpath 路徑提取 @class 為選取 class 屬性 lis = html.xpath("http://ul[@class='w980 pt20']/li") # 遍歷 Elements 節點 for li in lis: href = li.xpath(".//a/@href")[0] title = li.xpath(".//a/@title")[0] city = li.xpath(".//span[@class='w120 fl']/text()")[0].strip() ticket_price, time_interval, address_detail, score, other = get_detail( href) long_str = f"{href},{title},{city},{ticket_price},{time_interval},{address_detail},{score},{other}\n" save(long_str) except Exception as e: print("列表頁BUG") print(url) time.sleep(4) run(url) print(e) if __name__ == '__main__': urls = [] for i in range(1, 1313): urls.append(f"https://www.nyato.com/manzhan/?type=expired&p={i}") for url in urls: print(f"正在抓取{url}") run(url) print("全部爬取完畢")
上述代碼在提取漫展評分時,引入了 re 模塊,在字符串提取內容上,正則表達式的適配性還是比較高的。
通過正則直接從圖片地址中將評分提取出來,本案例可以這樣操作的原因是,分數圖片有規則可循,圖片連接如下,即分數圖片由分數連接而成。
# 3.0 分 //static.nyato.cn/expo-image/stars/star-3.0.png # 3.5 分 //static.nyato.cn/expo-image/stars/star-3.5.png
上述規則可通過不同的詳情頁,查看頁面源碼獲得。
下面是正則提取得分部分的代碼。
# 匹配得分 score_url = html.xpath("http://div[@class='mt10']/img/@src")[0] score = re.search(r"http://static.nyato.cn/expo-image/stars/star-(\d\.\d).png", score_url).group(1)
本篇博客的重點依舊在 lxml 模塊使用 XPath 提取數據部分。涉及到的代碼如下,其中重點關注如下列表內容:
// 從匹配選擇的當前節點選擇文檔中的節點;
[@class=xxxx] 匹配屬性;
@src 提取屬性值;
text() 匹配標簽內部文字。
其余需要掌握的,例如 ./ 選取當前節點,../ 選取當前節點的父節點。
address_detail = html.xpath("http://span[@class='fl mr10']/text()")[0] # 時間間隔 time_interval = html.xpath("http://div[@class='h25 line25 s6 f14 w100s mb10']/text()")[3].strip() # 票價 ticket_price = html.xpath("http://b[@class='f40']/text()")[0] # 匹配得分 score_url = html.xpath("http://div[@class='mt10']/img/@src")[0]
抓取結果展示時間
本次抓取的數據,已經上傳到 CSDN 下載頻道,源碼分享在代碼頻道。
完整代碼-:https://codechina.csdn.net/hihell/python120,NO12。
Python 網站
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。