Python 網絡爬蟲實戰:去哪兒網旅遊攻略圖文爬取保存爲 Markdown電子書

接上回,《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("&nbsp;", "")
    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 編輯器,也可以在評論區跟大家分享哦。

下載鏈接:https://www.typora.io/

 

0x04 後記

由於是單線程爬取,而且加了相對來說比較長的等待時間(主要也是時間寬裕,也不想給人家網站造成壓力)。

一下午時間爬了近2千篇文章,用 Typora 打開,翻閱起來真的是,感覺是真的爽。

 


 如果文章中有哪裏沒有講明白,或者講解有誤的地方,歡迎在評論區批評指正,或者掃描下面的二維碼,加我微信,大家一起學習交流,共同進步。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章