肝|打補丁,順便拓展下爬取公眾號歷史文章合并成一個PDF
↑↑↑關(guān)注后"星標"簡說Python
人人都可以簡單入門Python、爬蟲、數(shù)據(jù)分析 簡說Python發(fā)布 來源:簡說Python 作者:老表
大家好,我是老表!
本文是對前天發(fā)布的文章搞定,爬取公眾號文章轉(zhuǎn)換成PDF,自動郵件發(fā)送給自己!的修正。
寫在前面
搞定,爬取公眾號文章轉(zhuǎn)換成PDF,自動郵件發(fā)送給自己!文章發(fā)布后獲得了不少讀者朋友的好評,開心,不枉我回杭坐車一天寫下該文章,更開心的是有不少讀者朋友提出了一些問題,說明確實對大家有幫助,大家也實踐了,今天這篇文章就是對一位讀者發(fā)現(xiàn)的腳本bug的補丁。
很巧,又是在車上寫下的?。▋蓚€下班路上+加夜工寫完本文) 看到這里先劃到文章末尾給本文點個贊和在看,留言:優(yōu)秀,不過分吧,大家的支持就是我一直肝的動力。
開始動手動腦
首先明確下問題,這里也感謝下讀者另外半只??提出的問題:非二十次冪會員用戶登錄后也無法從教程中的接口中獲取數(shù)據(jù),也就無法獲取到公眾號文章鏈接和標題了,我嘗試新賬號登錄后發(fā)現(xiàn)確實是這樣,之前大意了!?。?/p>
知道問題后,我就開始想解決方法了,正好也有讀者加我微信了,問我相關(guān)問題,他已經(jīng)爬取到了微信公眾號文章鏈接和標題存在Excel中,他希望將內(nèi)容轉(zhuǎn)到word中,這我一想,這不正好互補嗎哈哈哈哈。
找讀者問了下他的爬取方法,他用的是之前文章提到的從微信公眾號后臺爬取,需要大家注冊微信公眾號,比較麻煩,所以最終略過了這個方案。
正在我一籌莫展之時,公眾號文章多了條留言。
志軍大佬提供了個新接口,可以從https://www.ershicimi.com/a/EOdxnBO4 這里獲取公眾號文章鏈接和標題,直呼優(yōu)秀!
分析新頁面
https://www.ershicimi.com/a/EOdxnBO4
訪問上方鏈接進入頁面發(fā)現(xiàn)不用登錄也能看到第一頁的數(shù)據(jù)了,直呼優(yōu)秀。(看到這里的讀者請先將 優(yōu)秀 二字 打在文末留言區(qū))
數(shù)據(jù)都在頁面上,我們直接利用requests發(fā)送一個get請求即可。
import requests
# bid=EOdxnBO4 表示公眾號 簡說Python,每個公眾號都有對應(yīng)的bid,可以直接搜索查看
url1 = 'https://www.ershicimi.com/a/EOdxnBO4'
r = requests.get(url1)
r.text
接下來我們需要解析網(wǎng)頁,從其中提取出我們需要的數(shù)據(jù):
最新發(fā)布文章標題 最新發(fā)布文章發(fā)布時間 最新發(fā)布文章對應(yīng)鏈接
解析網(wǎng)頁提取數(shù)據(jù)的方法很多,比較常用的有正則表達式、Xpath、CSS選擇器等,網(wǎng)頁標簽十分規(guī)則,我們利用Xpath來提取數(shù)據(jù)。
from lxml import etree
# 把html的文本內(nèi)容解析成html對象
html = etree.HTML(r.text)
# xpath 根據(jù)標簽路徑提取數(shù)據(jù)
html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/text()')
輸出:
[' 入門Pandas不可不知的技巧',
' 搞定,爬取公眾號文章轉(zhuǎn)換成PDF,自動郵件發(fā)送給自己!',
' PyPy為什么能讓Python原地起飛,速度比C還快!',
...
' 給大家分享一個私藏已久的Python神器!']
這樣我們就輕松的獲取到了公眾號簡說Python近期推文的標題,接下來我們還要獲取每篇文章對應(yīng)的 發(fā)布時間和鏈接。
在此之前我們先來說說代碼中的Xpath路徑是如何獲取到的,檢索原理是什么?
在頁面中,我們按住F12,調(diào)出瀏覽器的開發(fā)者工具,點擊左上角的元素選擇器,然后隨便選擇頁面中的一個文章標題,選中后,開發(fā)者工具中的Elements會自動跳動到對應(yīng)的頁面源碼處,如下圖所示:
選中源碼中對應(yīng)內(nèi)容,右鍵,選擇Copy中的Copy XPath,即可復(fù)制出對應(yīng)元素對應(yīng)的XPath路徑。
那如何獲取到所有標題對應(yīng)的XPath路徑呢?需要一個個通過XPath路徑來獲取對應(yīng)內(nèi)容嗎?
手動一個個復(fù)制肯定不切實際,其實很簡單,我們先按上面方法在復(fù)制一個標題的XPath路徑。
//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div[3]/div/h4/a
//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div[4]/div/h4/a
我們來觀察下,會很快發(fā)現(xiàn)兩個路徑的區(qū)別在于倒數(shù)第二個div標簽后面的數(shù)字不同,獲取所有標題內(nèi)容的方法就是直接給一個通用的XPath路徑,直接去掉倒數(shù)第二個div后的中括號和數(shù)字即可,除此外還需要在路徑后加上/text(),這樣就可以提取出對應(yīng)的內(nèi)容了。
//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/text()
同理我們可以獲取到發(fā)布時間和對應(yīng)文章鏈接的XPath通用路徑,并獲取對應(yīng)數(shù)據(jù),將之前代碼中的get_data函數(shù)代碼改為下面的即可,其他內(nèi)容不需要修改。
'''
1、從二十次冪獲取公眾號最新的推文鏈接和標題(新接口,解決必須會員才看的到數(shù)據(jù)問題)
'''
def get_data(publish_date):
# bid=EOdxnBO4 表示公眾號 簡說Python,每個公眾號都有對應(yīng)的bid,可以直接搜索查看
url1 = 'https://www.ershicimi.com/a/EOdxnBO4'
r = requests.get(url1)
# 把html的文本內(nèi)容解析成html對象
html = etree.HTML(r.text)
# xpath 根據(jù)標簽路徑提取數(shù)據(jù)
title = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/text()') # 標題
publish_time = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/p[2]/@title') # 發(fā)布時間
title_url = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/@href') # 文章鏈接
# 對數(shù)據(jù)進行簡單處理,選取最新發(fā)布的數(shù)據(jù)
data = []
for i in range(len(publish_time)):
if publish_date in publish_time[i]:
article = {}
article['content_url'] = 'https://www.ershicimi.com' + title_url[i]
article['title'] = title[i]
data.append(article)
return data
項目完整代碼可以查看之前的文章獲取,或者直接掃文末二維碼加我微信獲取。
拓展需求
爬取某個公眾號全部文章,轉(zhuǎn)成PDF教程集合
搞定了爬取最新文章轉(zhuǎn)換成PDF,再來做爬取某個公眾號全部文章,轉(zhuǎn)成PDF教程集合就簡單多了。
基本思路: 翻頁爬取所有文章鏈接和標題--然后依次轉(zhuǎn)換成pdf--合并所有PDF。
根據(jù)志軍大佬提供的接口爬取第一頁是沒問題的,翻頁會提示需要登錄,參考第一篇文章解釋,直接獲取Cookie破解這個反爬蟲措施是最簡單的,模擬登錄我也看了,有個動態(tài)參數(shù)csrf_token,有興趣的同學(xué)可以看看如何破解。
0)導(dǎo)入需要的庫
import requests # 發(fā)送get/post請求,獲取網(wǎng)站內(nèi)容
import wechatsogou # 微信公眾號文章爬蟲框架
import datetime # 日期數(shù)據(jù)處理模塊
import pdfkit # 可以將文本字符串/鏈接/文本文件轉(zhuǎn)換成為pdf
import os # 系統(tǒng)文件管理
import re # 正則匹配模塊
import sys # 項目進程管理
from lxml import etree # 把html的文本內(nèi)容解析成html對象
import time # 時間模塊
from PyPDF2 import PdfFileReader, PdfFileWriter # pdf讀取、寫入操作模塊
1)翻頁爬取所有文章鏈接和標題
代碼如下:
'''
1、從二十次冪獲取公眾號所有的推文鏈接和標題
'''
# 自動獲取公眾號文章總頁數(shù)
def get_page_num():
# bid=EOdxnBO4 表示公眾號 簡說Python,每個公眾號都有對應(yīng)的bid,可以直接搜索查看
url1 = 'https://www.ershicimi.com/a/EOdxnBO4'
r = requests.get(url1)
# 把html的文本內(nèi)容解析成html對象
html = etree.HTML(r.text)
# xpath 根據(jù)標簽路徑提取數(shù)據(jù)
page_num = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[2]/a/text()')
return int(page_num[-2]) # 放回倒數(shù)第二個元素,就是總頁數(shù)
# 將title,publish_time,content_url數(shù)據(jù)格式化成我們想要的形式
def merge_data(title,publish_time,content_url):
data = []
for i in range(len(title)):
html_data ={}
html_data['title'] = title[i]
html_data['publish_time'] = publish_time[i]
html_data['content_url'] = 'https://www.ershicimi.com' + content_url[i]
data.append(html_data)
return data
# 獲取數(shù)據(jù)
def get_data():
# 手動獲取登錄后的Cookie 保持登錄狀態(tài)
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
"Cookie":"登錄后自己獲取下,第一篇教程有詳細說明"
}
# bid=EOdxnBO4 表示公眾號 簡說Python,每個公眾號都有對應(yīng)的bid,可以直接搜索查看
# 循環(huán)獲取所有數(shù)據(jù)
page_num = get_page_num()
html_data = []
for i in range(page_num):
url1 = 'https://www.ershicimi.com/a/EOdxnBO4?page=%d'%(i+1)
print(url1)
r = requests.get(url1,headers=headers)
# 把html的文本內(nèi)容解析成html對象
html = etree.HTML(r.text)
# xpath 根據(jù)標簽路徑提取數(shù)據(jù)
title = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/text()') # 標題
publish_time = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/p[2]/@title') # 發(fā)布時間
content_url = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/@href') # 文章鏈接
html_data = html_data + merge_data(title,publish_time,content_url)
print("第%d頁數(shù)據(jù)獲取完成"%(i+1))
time.sleep(1) # 每隔1s 發(fā)送一次請求
return html_data
2)然后依次轉(zhuǎn)換成pdf
代碼不變,和第一篇教程一樣。
'''
2、for循環(huán)遍歷,將每篇文章轉(zhuǎn)化為pdf
'''
# 轉(zhuǎn)化url為pdf時,調(diào)用wechatsogou中的get_article_content函數(shù),將url中的代碼提取出來轉(zhuǎn)換為html字符串
# 這里先初始化一個WechatSogouAPI對象
ws_api = wechatsogou.WechatSogouAPI(captcha_break_time=3)
def url_to_pdf(url, title, targetPath, publish_date):
'''
使用pdfkit生成pdf文件
:param url: 文章url
:param title: 文章標題
:param targetPath: 存儲pdf文件的路徑
:param publish_date: 文章發(fā)布日期,作為pdf文件名開頭(標識)
'''
try:
content_info = ws_api.get_article_content(url)
except:
return False
# 處理后的html
html = f'''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{title}</title>
</head>
<body>
<h2 style="text-align: center;font-weight: 400;">{title}</h2>
{content_info['content_html']}
</body>
</html>
'''
# html字符串轉(zhuǎn)換為pdf
filename = publish_date + '-' + title
# 部分文章標題含特殊字符,不能作為文件名
# 去除標題中的特殊字符 win / \ : * " < > | ?mac :
# 先用正則去除基本的特殊字符,python中反斜線很煩,最后用replace函數(shù)去除
filename = re.sub('[/:*"<>|?]','',filename).replace('\\','')
pdfkit.from_string(html, targetPath + os.path.sep + filename + '.pdf')
return filename # 返回存儲路徑,后面郵件發(fā)送附件需要
3)合并所有PDF
合并PDF我們使用了PyPDF2這個庫,合并pdf的需求最早是合并自己的打車發(fā)票和行程單,代碼引用了CSDN山陰少年大佬的文章Python之合并PDF文件中的代碼,這里也非常適用,就直接拿過來了。
基本思路就是利用PyPDF2的PdfFileReader先依次讀取pdf內(nèi)容,然后利用PdfFileWriter寫入,PyPDF2更詳細用法和說明可以參考官方文檔。
'''
3、合并所有的pdf
'''
# 使用os模塊的walk函數(shù),搜索出指定目錄下的全部PDF文件
# 獲取同一目錄下的所有PDF文件的絕對路徑
def getFileName(filedir):
file_list = [os.path.join(root, filespath) \
for root, dirs, files in os.walk(filedir) \
for filespath in files \
if str(filespath).endswith('pdf')
]
file_list.sort() # 排序
return file_list if file_list else []
# 合并同一目錄下的所有PDF文件
def MergePDF(filepath, outfile):
output = PdfFileWriter()
outputPages = 0
pdf_fileName = getFileName(filepath)
if pdf_fileName:
for pdf_file in pdf_fileName:
# print("路徑:%s"%pdf_file)
# 讀取源PDF文件
input = PdfFileReader(open(pdf_file, "rb"))
# 獲得源PDF文件中頁面總數(shù)
pageCount = input.getNumPages()
outputPages += pageCount
# print("頁數(shù):%d"%pageCount)
# 分別將page添加到輸出output中
for iPage in range(pageCount):
output.addPage(input.getPage(iPage))
print("合并后的總頁數(shù):%d."%outputPages)
# 寫入到目標PDF文件
outputStream = open(os.path.join(filepath, outfile), "wb")
output.write(outputStream)
outputStream.close()
print("PDF文件合并完成!")
else:
print("沒有可以合并的PDF文件!")
隨便說說

只要大家支持,老表就能無限肝,大家如果有什么類似需求,或者想要本文所有相代碼的ipynb文件,可以掃下方二維碼添加我的微信,查看朋友圈獲取。
歡迎大家進行學(xué)習交流,需求描述越明確,被選中的機率越高。
參考鏈接
wechatsogou
https://github.com/Chyroc/WechatSogou二十次冪平臺
https://www.ershicimi.com/博客園xuzifan
https://www.cnblogs.com/xuzifan/p/11121878.htmlwkhtmltopdf下載地址
https://wkhtmltopdf.org/downloads.htmlSDN山陰少年大佬的文章Python之合并PDF文件
https://blog.csdn.net/jclian91/article/details/80362937搞定,爬取公眾號文章轉(zhuǎn)換成PDF,自動郵件發(fā)送給自己!
https://mp.weixin.qq.com/s/5ESrllgYExG0VJ9giCOp0w
掃碼即可加我微信
觀看朋友圈,獲取最新學(xué)習資源
學(xué)習更多: 整理了我開始分享學(xué)習筆記到現(xiàn)在超過250篇優(yōu)質(zhì)文章,涵蓋數(shù)據(jù)分析、爬蟲、機器學(xué)習等方面,別再說不知道該從哪開始,實戰(zhàn)哪里找了
“點贊”傳統(tǒng)美德不能丟 
