Python進(jìn)階多線程爬取網(wǎng)頁項目實戰(zhàn)
上一篇文章介紹了并發(fā)和多線程的概念,這次就來向大家上一個實戰(zhàn)來講解一下如何真正的運(yùn)用上多線程這個概念。
有需要的可以看看我之前這篇文章:Python進(jìn)階篇之多線程爬取網(wǎng)頁
一、網(wǎng)頁分析
這次我們選擇爬取的網(wǎng)站是水木社區(qū)的Python頁面
網(wǎng)頁:https://www.mysmth.net/nForum/#!board/Python?p=1
根據(jù)慣例,我們第一步還是分析一下頁面結(jié)構(gòu)和翻頁時的請求。
通過前三頁的鏈接分析后得知,每一頁鏈接中最后的參數(shù)是頁數(shù),我們修改它即可得到其他頁面的數(shù)據(jù)。
再來分析一下,我們需要獲取帖子的鏈接就在id 為 body 的 section下,然后一層一層找到里面的 table,我們就能遍歷這些鏈接的標(biāo)題。
我們點開一篇帖子:https://www.mysmth.net/nForum/#!article/Python/162717
和前面一樣,我們先分析標(biāo)題和內(nèi)容在網(wǎng)頁中的結(jié)構(gòu)
不難發(fā)現(xiàn),主題部分只要找到 id 為 main 的 section 下面的 class 為 b-head corner 的下面第二個 span即可
主題部分
而內(nèi)容部分只要找到class 為 a-wrap corner 的 div,找到下面的 a-content即可。
內(nèi)容部分
分析網(wǎng)頁結(jié)構(gòu)后,我們就可以開始寫代碼了!
二、代碼實現(xiàn)
首先要確定要保存什么內(nèi)容:這次我們保存水木社區(qū) Python 版面前 10 頁的所有帖子標(biāo)題和帖子第一頁的所有回復(fù)。
解析列表頁,得到所有的帖子鏈接
from bs4 import BeautifulSoup # 解析列表頁內(nèi)容,得到這一頁的內(nèi)容鏈接 def parse_list_page(text): soup = BeautifulSoup(text, 'html.parser') # 下面相當(dāng)于 soup.find('table', class_='board-list tiz').find('tbody') tbody = soup.find('table', class_='board-list tiz').tbody urls = [] for tr in tbody: td = tr.find('td', class_='title_9') urls.append(td.a.attrs['href']) return urls
解析內(nèi)容頁,得到標(biāo)題和這一頁的所有帖子內(nèi)容
# 解析內(nèi)容頁,得到標(biāo)題和所有帖子內(nèi)容 def parse_content_page(text): soup = BeautifulSoup(text, 'html.parser') title = soup.find('span', class_='n-left').text.strip('文章主題:').strip() content_div = soup.find('div', class_='b-content corner') contents = [] for awrap in content_div.find_all('div', class_='a-wrap corner'): content = awrap.p.text contents.append(content) return title, contents
把列表頁的鏈接轉(zhuǎn)換成我們要抓取的鏈接
def convert_content_url(path): URL_PREFIX = 'http://www.mysmth.net' path += '?ajax' return URL_PREFIX + path
生成前 10 頁的列表頁鏈接
list_urls = [] for i in range(1, 11): url = 'http://www.mysmth.net/nForum/board/Python?ajax&p=' url += str(i) list_urls.append(url)
下面是得到前 10 頁列表頁里所有正文的鏈接。這個時候我們使用線程池的方式來運(yùn)行
import requests from concurrent import futures session = requests.Session() executor = futures.ThreadPoolExecutor(max_workers=5) # 這個函數(shù)獲取列表頁數(shù)據(jù),解析出鏈接,并轉(zhuǎn)換成真實鏈接 def get_content_urls(list_url): res = session.get(list_url) content_urls = parse_list_page(res.text) real_content_urls = [] for url in content_urls: url = convert_content_url(url) real_content_urls.append(url) return real_content_urls # 根據(jù)剛剛生成的十個列表頁鏈接,開始提交任務(wù) fs = [] for list_url in list_urls: fs.append(executor.submit(get_content_urls, list_url)) futures.wait(fs) content_urls = set() for f in fs: for url in f.result(): content_urls.add(url)
在這里要注意一下,第 23 行中我們使用了 set() 函數(shù),作用是去除重復(fù)值。它的原理是創(chuàng)建一個 Set(集合),集合 是 Python 中的一種特殊數(shù)據(jù)類型,其中可以包含多個元素,但是不能重復(fù)。我們來看看 set() 的用法
numbers = [1, 1, 2, 2, 2, 3, 4] unique = set(numbers) print(type(unique)) # 輸出:<class 'set'> print(unique) # 輸出:{1, 2, 3, 4}
我們看到,set() 將列表 numbers 轉(zhuǎn)換成了沒有重復(fù)元素的集合 {1, 2, 3, 4}。
我們利用這個特性,首先在 23 行通過 content_urls = set() 創(chuàng)建了一個空集合,之后在其中添加鏈接時,就會自動去除多次出現(xiàn)的鏈接。
得到了所有正文鏈接之后,我們解析正文頁內(nèi)容,放到一個字典里
# 獲取正文頁內(nèi)容,解析出標(biāo)題和帖子 def get_content(url): r = session.get(url) title, contents = parse_content_page(r.text) return title, contents # 提交解析正文的任務(wù) fs = [] for url in content_urls: fs.append(executor.submit(get_content, url)) futures.wait(fs) results = {} for f in fs: title, contents = f.result() results[title] = contents print(results.keys())
就這樣,我們完成了多線程的水木社區(qū)爬蟲。打印 results.keys() 可以看到所有帖子的標(biāo)題。
這次爬取了前十頁的所有主題,以及他們的第一頁回復(fù)。一共 10 個列表頁、300 個主題頁,解析出 1533 條回復(fù)。在一臺網(wǎng)絡(luò)良好、性能普通的機(jī)器上測試執(zhí)行只花費(fèi)了 13 秒左右。
完整代碼如下
import requests from concurrent import futures from bs4 import BeautifulSoup # 解析列表頁內(nèi)容,得到這一頁的內(nèi)容鏈接 def parse_list_page(text): soup = BeautifulSoup(text, 'html.parser') tbody = soup.find('table', class_='board-list tiz').tbody urls = [] for tr in tbody: td = tr.find('td', class_='title_9') urls.append(td.a.attrs['href']) return urls # 解析內(nèi)容頁,得到標(biāo)題和所有帖子內(nèi)容 def parse_content_page(text): soup = BeautifulSoup(text, 'html.parser') title = soup.find('span', class_='n-left').text.strip('文章主題:').strip() content_div = soup.find('div', class_='b-content corner') contents = [] for awrap in content_div.find_all('div', class_='a-wrap corner'): content = awrap.p.text contents.append(content) return title, contents # 把列表頁得到的鏈接轉(zhuǎn)換成我們要抓取的鏈接 def convert_content_url(path): URL_PREFIX = 'http://www.mysmth.net' path += '?ajax' return URL_PREFIX + path # 生成前十頁的鏈接 list_urls = [] for i in range(1, 11): url = 'http://www.mysmth.net/nForum/board/Python?ajax&p=' url += str(i) list_urls.append(url) session = requests.Session() executor = futures.ThreadPoolExecutor(max_workers=5) # 這個函數(shù)獲取列表頁數(shù)據(jù),解析出鏈接,并轉(zhuǎn)換成真實鏈接 def get_content_urls(list_url): res = session.get(list_url) content_urls = parse_list_page(res.text) real_content_urls = [] for url in content_urls: url = convert_content_url(url) real_content_urls.append(url) return real_content_urls # 根據(jù)剛剛生成的十個列表頁鏈接,開始提交任務(wù) fs = [] for list_url in list_urls: fs.append(executor.submit(get_content_urls, list_url)) futures.wait(fs) content_urls = set() for f in fs: for url in f.result(): content_urls.add(url) # 獲取正文頁內(nèi)容,解析出標(biāo)題和帖子 def get_content(url): r = session.get(url) title, contents = parse_content_page(r.text) return title, contents # 提交解析正文的任務(wù) fs = [] for url in content_urls: fs.append(executor.submit(get_content, url)) futures.wait(fs) results = {} for f in fs: title, contents = f.result() results[title] = contents print(results.keys())
本次分享到此結(jié)束,謝謝大家閱讀??!
有問題歡迎評論區(qū)留言!!
更多關(guān)于Python多線程爬取網(wǎng)頁實戰(zhàn)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
把vgg-face.mat權(quán)重遷移到pytorch模型示例
今天小編就為大家分享一篇把vgg-face.mat權(quán)重遷移到pytorch模型示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12python實現(xiàn)漢諾塔遞歸算法經(jīng)典案例
這篇文章主要大家分享了python實現(xiàn)漢諾塔遞歸算法經(jīng)典案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05Python?BeautifulSoup4實現(xiàn)數(shù)據(jù)解析與提取
Beautiful?Soup是一個Python的庫,用于解析HTML和XML文檔,提供了方便的數(shù)據(jù)提取和操作功能,下面小編就來和大家詳細(xì)聊聊如何利用BeautifulSoup4實現(xiàn)數(shù)據(jù)解析與提取吧2023-10-10Python3 列表,數(shù)組,矩陣的相互轉(zhuǎn)換的方法示例
這篇文章主要介紹了Python3 列表,數(shù)組,矩陣的相互轉(zhuǎn)換的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08基于python SMTP實現(xiàn)自動發(fā)送郵件教程解析
這篇文章主要介紹了基于python實現(xiàn)自動發(fā)送郵件教程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06Django項目主urls導(dǎo)入應(yīng)用中views的紅線問題解決
這篇文章主要介紹了Django項目主urls導(dǎo)入應(yīng)用中views的紅線問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-08-08python正則表達(dá)式re.search()的基本使用教程
正則表達(dá)式是我們?nèi)粘i_發(fā)中必然會遇到的,下面這篇文章主要介紹了python正則表達(dá)式re.search()的基本使用教程,需要的朋友可以參考下2021-05-05