接上回,《Python 網絡爬蟲實戰:爬取《去哪兒》網數千篇旅遊攻略數據》。
我們爬取到了數千篇的旅遊攻略文章的數據。
但是事情還沒有結束,對於大部分的人來講,最希望得到的東西應該不是這種乾巴巴的 Excel 數據,
而是這種圖文並茂的文章吧!
其實之前我們爬過很多類似的網站,比如 《人民日報新聞爬蟲》,《知乎問題回答爬蟲》,都是爬取大段的文章。
不過區別在於,那些爬蟲的關注點在於文字,主要用來做分詞,語義情感等方面的分析,也就是說不是給人看的,是給程序看的,所以直接將圖片,超鏈接,排版格式等東西捨棄,僅提取文字,使用記事本保存即可。
而這篇爬蟲不同,爬取旅遊攻略文章,重點在於閱讀體驗,如果拋棄了圖片,拋棄了排版,爬到的攻略文章也就失去了靈魂。
BUT,用什麼格式的文件可以保存圖文,還可以儘可能保留原始排版呢?想來想去,我覺得 Markdown 或許是最佳選擇。
0x00 分析網站
相比於上一篇爬蟲中各式各樣的數據,這篇爬蟲要爬的內容就簡單很多了。
以 https://travel.qunar.com/travelbook/note/6910266 這篇文章爲例,使用 開發者工具(F12)來進行分析。
可以看到,文章的正文部分是在一個 <div class='e_main' id='tpl_1'> 標籤中的,其中每一個子 div 標籤存放一個章節的內容。
分析完畢,是不是確實很簡單呢?
如果是按照之前的做法,我可能直接一個 '.text' 或者 '.string' ,把其中的文字提取出來就完事兒了。
但是這裏我們不能這樣做,需要把它完整地取出來,保留其原本的結構,轉換成 Markdown 格式進行存儲。
0x01 將 HTML 轉成 Markdown 格式
這裏我結合使用了 BeautifulSoup 和 html2text 庫。
BeautifulSoup 庫用來定位提取文章的正文部分,html2text 庫用來將正文部分的 html 文本轉化成 markdown 格式。
1. 提取正文部分
import requests
from bs4 import BeautifulSoup
def getContent(html):
'''
提取文章的正文部分的 html
'''
bsObj = BeautifulSoup(html,'lxml')
title = bsObj.find("h1").text
content = bsObj.find("div",attrs = {"id":"tpl_1"})
return str(content)
url = 'https://travel.qunar.com/travelbook/note/6910266'
html = fetchUrl(url) # fetchUrl(url) 函數用於發起網絡請求
content = getContent(html)
print(content)
運行結果:
網站沒有設置過多的反爬機制,成功獲取到文章的正文部分。
2. 將正文部分的 html 轉換成 Markdown 格式
這部分主要是通過 html2text 庫來完成,不過該庫在轉換過程中,有一些轉換錯誤的地方,需要對轉換結果做一定的處理。(以下是我在用 html2text 庫轉換去哪兒網站攻略文章時出現的問題,不知道是庫有問題還是網站的問題,大家使用的話根據實際情況進行調整)
① 章節標題格式
html 中的 h 標籤,轉換成 Markdown 後,會在 # 後面多兩個換行符。
如 h1 標籤會轉換成 "#\n\n",而實際我們需要的是 "# "( # 後面加空格)
text = text.replace("#\n\n", "# ")
有些標題是有超鏈接的(網頁中查看時,鼠標移上去會有 Tips 框彈出),這些Tips信息轉換成 Markdown 格式後顯示會有點混亂。我們直接將其超鏈接去除,只保留純粹的標題文字。
header5 = content.find_all("h5")
for h5 in header5:
t5 = h5.find("div", attrs = {"class":"b_poi_title_box"})
h5.insert_after("##### " + t5.text)
h5.extract()
② 莫名其妙的換行符
可能是網頁源碼中有一些特殊的字符,轉換後出現了很多換行符。
text = text.replace("\\.",".")
text = text.replace(".\n",". ")
text = text.replace("tr-\n","tr-")
③ 不需要的標籤
文章正文部分中夾雜着一些標籤,比如下圖中的 “評論” ,是我們不需要的,可以將其處理去掉。
我們可以在轉換前,直接使用 BeautifulSoup 的 extract 函數將其剔除。
cmts = content.find_all("div", attrs = {"class":"ops"})
for s in cmts:
s.extract()
④ 正文中出現了Markdown 格式控制符號
有些文章中的文字比較活潑可愛,用了很多顏文字,比如 ~~~ ^_^ ~~~ 等,而 ~~~ ,``,* 等這些符號是 Markdown 中用來控制格式的符號,導致雖然文章轉換沒什麼問題,但是顯示出現了問題。
# 正文中 ~ 的個數不確定,經過觀察這樣大概就基本可以正確顯示了。
html = html.replace("~~", "~").replace("~~", "~")
等等等等,還有其他細節方面的調整,其實也不算是共性問題,分享出來可能意義也不是很大,就不一一列舉了,大家遇到了的話針對性地調整就可以了。
0x02 完善代碼開始爬取
1. 讀取 URL 列表
這裏我們直接從上篇文章中爬取得到的 csv 文件中讀取(大家感興趣的可以去看看,跑跑文章中的代碼就可以很容易得到)。
爲了方便,我這裏上傳了一份測試用的文檔(下載鏈接),大家可以去下載使用。(CSDN的下載需要積分,大家有積分的權當支持一下博主啦!如果鏈接失效了,或者沒有下載積分,可以在文末加我微信找我要)
import pandas as pd
df = pd.read_csv('data.csv', sep = ',', usecols = [0,1])
for index, title, url in df.itertuples():
print(title)
print(url)
運行結果:
可以讀取到每篇文章的標題和鏈接。
2. 發起網絡請求
下面是 fetchUrl 函數,用於發起網絡請求。
import requests
def fetchUrl(url):
'''
發起網絡請求
'''
headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36',
}
r = requests.get(url,headers=headers)
r.raise_for_status()
r.encoding = "utf-8"
return r.text
3. 爬取正文並轉換成 Markdown 格式
getContent 函數用來從網頁源碼中提取正文部分的 Html 文本,並進行一些簡單的預處理。
包括對圖片,標題格式的替換,無關標籤的剔除,以及一些有干擾的特殊字符的替換。
from bs4 import BeautifulSoup
def getContent(html):
'''
提取文章的正文部分的 html
'''
html = html.replace(" ", "")
html = html.replace("~~", "~").replace("~~", "~")
bsObj = BeautifulSoup(html,'lxml')
title = bsObj.find("h1").text
content = bsObj.find("div",attrs = {"id":"b_panel_schedule"})
imgs = content.find_all("img")
for img in imgs:
src = img['data-original']
txt = img['title']
img.insert_after("![{0}]({1})".format(txt,src))
img.extract()
header5 = content.find_all("h5")
for h5 in header5:
t5 = h5.find("div", attrs = {"class":"b_poi_title_box"})
#print(t5.text)
h5.insert_after("##### " + t5.text)
h5.extract()
cmts = content.find_all("div", attrs = {"class":"ops"})
for s in cmts:
s.insert_after('< br/>')
s.extract()
return str(content)
Html2Markdown 函數,主要作用是將 html 文本轉換成 Markdown 格式,並對轉換過程中出現的一些格式錯誤進行修正。
import html2text as ht
def Html2Markdown(html):
'''
將文章正文部分由 html 格式轉換成 Markdown 格式
'''
text_maker = ht.HTML2Text()
text = text_maker.handle(html)
text = text.replace("#\n\n", "# ")
text = text.replace("\\.",".")
text = text.replace(".\n",". ")
text = text.replace("< br/>","\n")
text = text.replace("tr-\n","tr-")
text = text.replace("查看全部 __","")
return text
4. 保存文件
我們保存文件時,使用文章標題作爲文件名存儲。而文件名中有一些字符,如正反斜槓 / \ ,英文引號 ' ",英文大於小於號 <> 等等,我們需要對其進行剔除,或者替換成中文的符號。否則會報錯,保存失敗。
import os
def saveMarkdownFile(title,content):
'''
保存文本到 Markdown 文件中
title:文件名
content:要保存的文本內容
'''
# 剔除或替換文件名中不允許出現的符號
title = title.replace("\\","")
title = title.replace("/","")
title = title.replace("\"","”")
title = title.replace("\'","’")
title = title.replace("<","《")
title = title.replace(">","》")
title = title.replace("|","&")
title = title.replace(":",":")
title = title.replace("*","x")
title = title.replace("?","?")
with open("data/" + title + ".md", 'w', encoding='utf-8') as f:
f.write(content)
5. 爬蟲調度器
最後我們需要寫一個爬蟲調度的函數,來啓動並控制我們的爬蟲。
import time
from random import randint
def main():
df = pd.read_csv('data.csv', sep = ',', usecols = [0,1])
for index, title, url in df.itertuples():
html = fetchUrl(url)
content = getContent(html)
md = Html2Markdown(content)
saveMarkdownFile(title, md)
# 隨機等待時間,避免爬取過於頻繁觸發反爬機制
t = randint(0,3)
print("wait -- ",str(t),"s")
time.sleep(t)
# 啓動爬蟲
main()
print("爬取完成!")
上述就是本次爬蟲的全部源碼了。
0x03 問題解決
1. 如何安裝 html2text 庫?
雖然我相信這個小問題一定難不倒聰明的大家的,但是這裏還是講一下,給大夥兒節省點時間,哈哈。
安裝命令是:
pip install html2text
如果上面那個指令安裝時,提示 ConnectTimeoutError 連接超時失敗(反正我是連接超時失敗了),可以試一下下面這個指令。
pip install html2text -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
參考鏈接:https://blog.csdn.net/licheetools/article/details/82946342
2. 什麼是 Markdown ?如何打開 Markdown 文件?
Markdown 的基本概念我就不說了,自己去網上搜吧。就相當於 Office Word 的精簡版,可以讓你像寫代碼一樣寫文章,用熟練了,寫起東西來非常絲滑。
我目前在用的一款 Markdown 編輯器,叫 Typora,界面還是非常乾淨漂亮的,顏值很高。在這裏給大家簡單推薦一下,如果大家有什麼好用的 Markdown 編輯器,也可以在評論區跟大家分享哦。
0x04 後記
由於是單線程爬取,而且加了相對來說比較長的等待時間(主要也是時間寬裕,也不想給人家網站造成壓力)。
一下午時間爬了近2千篇文章,用 Typora 打開,翻閱起來真的是,感覺是真的爽。
如果文章中有哪裏沒有講明白,或者講解有誤的地方,歡迎在評論區批評指正,或者掃描下面的二維碼,加我微信,大家一起學習交流,共同進步。