06 Python3.6爬取全言乒乓連載文章并保存pdf和txt電子書

序言

這是一篇集世界乒乓球運(yùn)動技術(shù)演進(jìn)显拜、發(fā)展和不同歷史階段著名運(yùn)動員簡介的百度貼吧帖子吃媒。作者全言是我很關(guān)注的一個吧友瓤介,我本科時候追過的一個他在百度貼吧乒乓球吧連載的一個長帖子吕喘,彼時我還是個乒乓小白,看帖子覺得他講技術(shù)演進(jìn)講得特別透徹刑桑,但是他有點問題氯质,老是對自己寫的東西不滿意,總是刪帖建帖刪帖建帖復(fù)制粘貼復(fù)制粘貼祠斧,我的貼吧收藏夾都跟丟了好幾次闻察,后來又去乒乓網(wǎng)、百家號搞連載琢锋,后來也都逐漸停更辕漂,后來他建了一個公眾號,把這個連載帖放在里面吴超,但是每篇文章都太短了钉嘹,需要點進(jìn)去退出來再點進(jìn)去,所以我用Python寫了一個爬蟲腳本鲸阻,把他這一系列的幾百多篇文章給導(dǎo)入成txt電子書文檔了跋涣。

資源

代碼相關(guān)準(zhǔn)備工作

  • 任務(wù)
1. 爬取以下兩個網(wǎng)頁:網(wǎng)頁1網(wǎng)頁2里面的所有文章鏈接遣臼,放入一個列表性置,再傳遞給下一步;
  • 思路:

這一階段主要利用selenium來模擬Chrome瀏覽器獲取所有的文章鏈接揍堰。首先要模擬點擊不同的頁內(nèi)標(biāo)簽(如紅色標(biāo)注所示)鹏浅,但是由于每個標(biāo)簽下只默認(rèn)顯示十條,只有向下滾動觸發(fā)js才能加載頁內(nèi)剩余的條目屏歹,這個過程屬于異步加載隐砸。

模擬點擊不同的頁內(nèi)標(biāo)簽(如紅色標(biāo)注所示)

  • 分析實現(xiàn)
    這種規(guī)模的問題,一般會使用Beautifulsoup庫+XHR調(diào)試或者selenium.webdriver蝙眶,但是Beautifulsoup庫+XHR調(diào)試有問題季希,在頁面下滾捕捉query的時候,看起來像是有什么微妙的規(guī)律幽纷,但是真正更改query參數(shù)的時候式塌,打開的網(wǎng)頁還是一模一樣,我不得其解友浸,多究無益峰尝,果斷止損放棄。
    更改query參數(shù)后收恢,打開的網(wǎng)頁還是一模一樣武学,我不得其解祭往,多究無益,果斷放棄

    于是敲定使用selenium火窒。
  1. 列表中讀取文章鏈接硼补,打開鏈接,抓取段落存入txt文件對象熏矿,網(wǎng)頁利用weasyprint庫直接轉(zhuǎn)pdf已骇;
  • 思路:這一步給定了文章鏈接,由于Beautifulsoup的速度比selenium要快(selenium要打開瀏覽器)曲掰,我采用Beautifulsoup
  1. pdf合并奈辰。
    使用Pypdf2中的PdfFileMerger方法(from PyPDF2 import PdfFileMerger)合并pdf栏妖,但是這種方法不帶書簽。
    如果執(zhí)意添加書簽超鏈接奖恰,需要from PyPDF2 import PdfFileReader, PdfFileWriter然后一遍addPage一邊調(diào)用addBookmark吊趾,具體使用方法參考
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import os
import requests
from bs4 import BeautifulSoup
from weasyprint import HTML
import ssl
from PyPDF2 import PdfFileReader, PdfFileWriter, PdfFileMerger


outpath = './Table Tennis 24Years'  #輸出到根目錄指定文件夾,如果沒有就創(chuàng)目錄
if not os.path.exists(outpath):
    os.makedirs(outpath)

outpathpdf = './Table Tennis 24Years/PDF_folder'
if not os.path.exists(outpathpdf):
    os.makedirs(outpathpdf)


#打開瀏覽器
# 運(yùn)行前先下載 chrome driver,下載地址是:https://sites.google.com/a/chromium.org/chromedriver/downloads瑟啃,點擊【Latest Release: ChromeDriver x.xx】進(jìn)入下載
driver = webdriver.Chrome(executable_path='/Users/miraco/PycharmProjects/grabnet/chromedriver')  # Windows 需寫成'./chromedriver.exe'
driver.start_client()  #網(wǎng)頁需要模擬瀏覽器點擊

url_pages = ('https://mp.weixin.qq.com/mp/homepage?__biz=MzI5MjY0MTY1Ng==&hid=2&sn=858963d6283870bc173bbb7076a4e620&scene=25#wechat_redirect',
            'https://mp.weixin.qq.com/mp/homepage?__biz=MzI5MjY0MTY1Ng==&hid=6&sn=53bfd170c878ae8b06c868cf8c5c4e34&scene=25#wechat_redirect'
            )  #這是這兩個目標(biāo)網(wǎng)頁的網(wǎng)址论泛,我們要把網(wǎng)址里面的所有文章爬出來
tops_css = '#namespace_1 > div.tab_hd > div > div'   #上方目錄表標(biāo)簽樣式
titles_css = '#namespace_1 > div.tab_bd > div > a > div.cont > h2'   #標(biāo)簽下的題目的樣式
hrefs_css = '#namespace_1 > div.tab_bd > div > a'   #每個標(biāo)簽下的超鏈接樣式
info_css  = '#namespace_1 > div.tab_bd > div > a > div.cont > p'     #
all_list = [] #這里面放所有文章的題目、鏈接蛹屿、簡介

def pgdown():  #頁面往下翻滾直到盡頭屁奏,多次翻滾保證完全加載
    html_page = driver.find_element_by_tag_name('html') #拿到網(wǎng)頁對象
    for i in range(8):
        time.sleep(0.5)
        html_page.send_keys(Keys.END)     #模擬對著網(wǎng)頁按下鍵盤'END'的動作


def find_art(url):  #要爬取給定url中的文章的題目、簡介错负、超鏈接
    lists = []  #這個列表里放要此url可達(dá)的文章的題目坟瓢、梗概、鏈接
    driver.get(url)  #打開其中一個網(wǎng)頁
    time.sleep(3)   #等待網(wǎng)頁加載
    buttons = driver.find_elements_by_css_selector(tops_css)  #找到上方目錄表標(biāo)簽
    for button in buttons:   #按個激活標(biāo)簽
        time.sleep(2)    #等待網(wǎng)頁加載
        button.click()   #點擊標(biāo)簽
        pgdown()         #往下滾頁
        titles = driver.find_elements_by_css_selector(titles_css)  #找到所有每個標(biāo)簽下的題目對象
        hrefs = driver.find_elements_by_css_selector(hrefs_css)    #找到每個標(biāo)簽下的超鏈接對象
        intros = driver.find_elements_by_css_selector(info_css)    #找到每個題目下的簡介對象
        for title, href, intro in zip(titles,hrefs,intros):
            txt = title.text          #題目對象轉(zhuǎn)文本
            if '):' in txt:          #因為正經(jīng)文章題目有括號冒號字樣犹撒,可以依此只找正經(jīng)編號文章折联,不找其他
                ref = href.get_attribute('href')    #超鏈接對象中提取超鏈接
                lists.append([txt,ref,intro.text])  #符合要求的題目、超鏈接识颊、簡介作為一個子列表诚镰,放入大列表中
    return lists

for url in url_pages:  #這是這兩個目標(biāo)網(wǎng)頁的網(wǎng)址,都爬出來
    all_list = all_list + find_art(url)
    #得到的是[[a,b,c],[d,e,f],[,g,h,i],[j,k,l]]
    #這里不能用append方法祥款,因為用append以后得到的是[[[a,b,c],[d,e,f]],[[,g,h,i],[j,k,l]]]

driver.quit()  #關(guān)瀏覽器

print(all_list)   #這里打印放所有文章的題目清笨、鏈接、簡介

#爬取到txt

#建立或?qū)σ延械拇嗣鹴xt進(jìn)行內(nèi)容清空
f = open(os.path.join(outpath,'Table Tennis 24 Years.txt'),'w')
f.close()

#開寫開爬刃跛,這里爬去使用selenium打開關(guān)閉瀏覽器太慢了函筋,直接上Beautifulsoup,嗖嗖的
f = open(os.path.join(outpath,'Table Tennis 24 Years.txt'),'a') #打開文件對象
f.write('本文檔內(nèi)所有文章皆由"全言乒乓"撰寫奠伪,Sober作為乒乓球迷苦于其內(nèi)容支離分散跌帐,使用基于Python3.6網(wǎng)絡(luò)爬蟲工具進(jìn)行文字整理首懈,版權(quán)屬于"全言乒乓",如侵權(quán)請聯(lián)系我刪除!\n\n\n')

def web2txt(f,url,intro):  #給定txt對象谨敛、文章鏈接究履、簡介,將其寫入文件
    web_page = requests.get(url)
    soup = BeautifulSoup(web_page.text,'lxml')
    title = soup.select('h2.rich_media_title')[0] #抓取文章頁內(nèi)的題目
    f.write(title.text.strip() + ':' + intro.strip() + '\n\n')  #題目+簡介寫進(jìn)文件
    parapraghs = [i.text.strip() for i in soup.select('#js_content > p > span') if i.text.strip() != '' ]  #抓取段落列表并文本化脸狸,strip()去掉前后多余的空格
    for paragraph in parapraghs:
        if  '微信公眾號' not in paragraph:      #判斷本段是不是頁末的廣告
            f.write(paragraph.strip()+'\n\n')    #不是廣告才寫進(jìn)去
        else:
            f.write('\n------本節(jié)完------'+'\n\n')    #到廣告了寫上"本節(jié)完"
            break
    return f

ssl._create_default_https_context = ssl._create_unverified_context    #weasyprint有時候強(qiáng)制要求ssl最仑,但是有時候會抽風(fēng)犯錯,為了避免ssl證書出問題炊甲,我們禁用ssl

for title ,url, intro in all_list:
    print(f'正在整理文章:{title}')  #表明進(jìn)度
    f = web2txt(f,url,intro)  #寫txt泥彤,并依照題目命名
    HTML(url).write_pdf(os.path.join(outpathpdf,f'{title}.pdf')) #寫pdf,并依照題目命名

f.close() #關(guān)閉文件對象卿啡,得到txt文件


#再將pdf合并輸出

filelist = os.listdir(outpathpdf)   #讀取文件夾里的文件名
pdfs = [ os.path.join(outpathpdf,file) for file in filelist if not os.path.isdir(file)]   #摘取文件里的pdf放進(jìn)列表
pdfs.sort(key = lambda x : int(x.split('(')[1].split(')')[0])) #并按里面的數(shù)字排序吟吝,注意不能粗暴直接sort()排序,否則會出現(xiàn)10排在2前面的情況
print(pdfs)

#這段代碼是直接合并pdf颈娜,不帶書簽的
'''
merger = PdfFileMerger()
for pdf in pdfs:
    merger.append(pdf)  #按pdf順序合并
merger.write(os.path.join(outpath,'Table Tennis 24 Years.pdf'))  #合并輸出
'''
#這段代碼是逐頁合并pdf剑逃,而且有超鏈接書簽的
output  = PdfFileWriter()
output_Pages = 0  #文檔總頁數(shù)

for pdf in pdfs:
    input = PdfFileReader(open(pdf,'rb'))  #打開讀取文檔
    pdf_name = pdf.split('/')[-1]    #拿到文件名稱作為書簽名
    page_Count = input.getNumPages()    #讀取當(dāng)前小pdf的頁數(shù)
    output_Pages += page_Count     #總頁數(shù)累加計數(shù)
    for iPage in range(page_Count):
        output.addPage(input.getPage(iPage))   #小pdf內(nèi)逐頁添加到輸出pdf對象
    output.addBookmark(pdf_name,pagenum =output_Pages-page_Count,parent=None)  #在小pdf的首頁添加書簽

output.write(open(os.path.join(outpath,'Table Tennis 24 Years.pdf'), 'wb'))     #合并輸出

運(yùn)行結(jié)果

  • txt
txt
  • pdf
pdf

踩過的坑

  • phantomJS

我一開始想要使用網(wǎng)頁截圖再轉(zhuǎn)pdf,但是Webdriver的Chrome網(wǎng)頁截圖不支持滾動截圖官辽。其實selenium有兩種形式蛹磺,有頭的和無頭(headless)的,我用的是有頭的瀏覽器同仆,在以前開發(fā)者喜歡用的是PhantomJS嫩码,但是selenium不知道搞什么鬼懈凹,竟然在運(yùn)行phantomJS時候提示最新版本的selenium停止支持js,建議我使用Chrome或者Firefox的無頭瀏覽器,
無頭瀏覽器就是沒有界面的靜默版本诀艰,也可以調(diào)用坷牛,但是肉眼看不見芯侥,不喜歡界面打擾的可以試試看下面的代碼缠犀。

chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
driver = webdriver.Chrome(executable_path='./chromedriver',chrome_options=chrome_options)
  • pdfkit

其實網(wǎng)頁轉(zhuǎn)pdf的庫還有一個叫pdfkit,要預(yù)裝wkhtmltopdf臭觉,而且轉(zhuǎn)換效果很差昆雀,還不如這個庫呢,不過weasyprint雖說效果更好蝠筑,但是也是不支持異步加載的狞膘,有人在此項目的Github主頁里issue了為什么不能加載微信文章的插圖,作者也提到了這個問題什乙,本庫不支持js異步加載挽封。

  • 爬取文章鏈接時的異步加載元素問題

在檢測文章的入口的css元素樣式的時候,如果點擊了頁上文章列表的新標(biāo)簽臣镣,那么在elements中所查找的css元素個數(shù)會增多辅愿,但是并不意味著你可以把列表標(biāo)簽挨個點擊以后使用find_elements_by_css_selector方法一網(wǎng)打盡智亮,你確實可以拿到元素,但是使用元素對象使用text方法以后点待,你發(fā)現(xiàn)只能從當(dāng)前激活列表標(biāo)簽下的元素里拿出數(shù)據(jù)阔蛉,不在當(dāng)前頁面的數(shù)據(jù)拿不出來,是空字符串''癞埠,所以只能點擊一次拿一次状原。類似有人問過這樣的問題,就是元素拿不到了苗踪。

因此颠区,如果得到的文本只為空,那么當(dāng)前定位的元素可能被隱藏了通铲。

  1. 判斷是否被隱藏 毕莱。 driver.find_element_by_xx().is_displayed() ,如果得到 false的結(jié)果.那就說明被隱藏了测暗。
  2. 怎么解決央串?is_displayed()為false的元素,依然可以通過getAttribute()方法獲取元素的屬性. 由于webdriver spec的定義磨澡,Selenium WebDriver 只會與可見元素交互碗啄,所以獲取隱藏元素的文本總是會返回空字符串∥壬悖可是稚字,在某些情況下,我們需要獲取隱藏元素的文本厦酬。這些內(nèi)容可以使用element.attribute('attributeName'), 通過textContent, innerText, innerHTML等屬性獲取胆描。
  • weasyprint庫直接轉(zhuǎn)pdf時候ssl報錯
weasyprint.urls.URLFetchingError: 
URLError: <urlopen error [SSL:CERTIFICATE_VERIFY_FAILED] 
certificate verify failed (_ssl.c:777)

解決辦法;禁用ssl

import ssl
ssl._create_default_https_context = ssl._create_unverified_context
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仗阅,一起剝皮案震驚了整個濱河市昌讲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌减噪,老刑警劉巖短绸,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筹裕,居然都是意外死亡醋闭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門朝卒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來证逻,“玉大人,你說我怎么就攤上這事抗斤∏羝螅” “怎么了丈咐?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長洞拨。 經(jīng)常有香客問我扯罐,道長,這世上最難降的妖魔是什么烦衣? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任歹河,我火速辦了婚禮,結(jié)果婚禮上花吟,老公的妹妹穿的比我還像新娘秸歧。我一直安慰自己,他們只是感情好衅澈,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布键菱。 她就那樣靜靜地躺著,像睡著了一般今布。 火紅的嫁衣襯著肌膚如雪经备。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天部默,我揣著相機(jī)與錄音侵蒙,去河邊找鬼。 笑死傅蹂,一個胖子當(dāng)著我的面吹牛纷闺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播份蝴,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼犁功,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了婚夫?” 一聲冷哼從身側(cè)響起浸卦,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎案糙,沒想到半個月后限嫌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侍筛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年萤皂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匣椰。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡裆熙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情入录,我是刑警寧澤蛤奥,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站僚稿,受9級特大地震影響凡桥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚀同,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一缅刽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蠢络,春花似錦衰猛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至髓霞,卻和暖如春卦睹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背方库。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工结序, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人薪捍。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓笼痹,卻偏偏與公主長得像配喳,于是被迫代替她去往敵國和親酪穿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容