Selenium 根据输入的关键字来爬取微信公众号文章并保存为pdf与长图片
- 上班搭地铁的时候太无聊了,想在微信看一些感兴趣的学习文章,却不知道如何精准查看。实在无聊的我,查询了一些学习资料,然后做了本期博客。本期博客主要的内容是根据自己输入的关键字,去搜狗微信爬取大量的文章,然后保存为pdf以及长图片,我再保存到手机慢慢看,哈哈哈。
- 本期博客的代码基本是通用,只需要输入你要学习的内容的关键字,找到保存按钮的位置,自定义保存pdf与长图片的路径,即可得到1000篇对应的公众号文章的pdf以及长图片。
- 严正声明:本博客仅作学习教程,重在介绍如何使用各种便利的工具来达到学习的目的!不建议大家爬取太大的数据量的文章,虽然一次也最多爬取1000篇。
话不多说,让我步入整体。
1、搭建环境:Selenium+ChromeDriver+pyautogui
1.1 安装包
pip install selenium
pip install pyautogui
1.2、查看你的浏览器的版本
在网址栏输入:chrome://version
我的浏览器的版本是85.0.4183。
1.3、下载对应版本的ChromeDriver驱动
网址如下:http://chromedriver.storage.googleapis.com/index.html 因为我的谷歌版本是85.0.4183,所以我下载的ChromeDriver驱动对应的也是85.0.4183版本的。
1.4、设置环境变量
第一步:把下载好的zip压缩包解压,然后把里面的chromedriver.exe复制到谷歌浏览器的安装目录去。忘记谷歌浏览器的安装目录的人可以在1.1的图片那看可执行文件路径那里。 第二步:复制成功后,把这个目录复制一遍,添加到系统的环境变量里的path。
2、配置参数,使用驱动打开谷歌浏览器
运行工具:Jupyter NoteBook
from selenium import webdriver
import json
# 设置保存为pdf的参数
appState = """{
"recentDestinations": [
{
"id": "Save as PDF",
"origin": "local"
}
],
"mediaSize": {
"height_microns": 279400,
"name": "NA_LETTER",
"width_microns": 215900,
"custom_display_name": "Letter"
},
"selectedDestinationId": "Save as PDF",
"version": 2,
"isHeaderFooterEnabled": false
}"""
appState = json.loads(appState)
profile = {"printing.print_preview_sticky_settings.appState": json.dumps(appState)}
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option('prefs', profile)
# 该参数必须存在,不设置会无法使用打印,保存全屏图片等功能
chrome_options.add_argument('--kiosk-printing')
# 打开一个浏览器窗口
driver = webdriver.Chrome(options=chrome_options)
其中,需要注意isHeaderFooterEnabled这个参数,如果为False,则为不打印页眉页脚的意思,因为我后面pdf转全屏图片的时候,存在页眉页脚会影响阅读,就取消了。
3、扫码登录搜狗微信
import time
# 登录搜狗微信
driver.get("https://weixin.sogou.com/")
time.sleep(3)
driver.find_elements_by_xpath('//a[@id="loginBtn"]')[0].click()
执行上面的代码时,会出现一个二维码,你需要使用微信扫码登录一下。注意,这一步非常重要,如果你没有扫码登录,那么很有可能在接下来爬取文章信息的时候会被检测出是爬虫,会弹出验证而导致爬取失败。而且你不登录,能爬取的学习文章才10页,登录的话可以爬取100页。
4、输入关键字,点击搜索按钮
比如我想学习一些关于selenium的文章,那么我就输入这个关键字就行
# 输入你要收集的学习文章的搜索关键字“XXX”
query = driver.find_element_by_name("query")
query.send_keys("selenium")
# 点击搜索按钮
time.sleep(1)
driver.find_elements_by_xpath('//input[@uigs="search_article"]')[0].click()
5、爬取文章,并保存为pdf
import pyautogui
def scroll_to_bottom(driver):
js = "return action=document.body.scrollHeight"
# 初始化现在滚动条所在高度为0
height = 0
# 当前窗口总高度
new_height = driver.execute_script(js)
while height < new_height:
# 将滚动条调整至页面底部
for i in range(height, new_height, 100):
driver.execute_script('window.scrollTo(0, {})'.format(i))
time.sleep(0.5)
height = new_height
time.sleep(2)
new_height = driver.execute_script(js)
driver.find_elements_by_xpath('//a[@uigs="article_title_{}"]'.format(0))[0].click()
driver.switch_to.window(driver.window_handles[1])
time.sleep(1)
title = driver.find_elements_by_xpath('//*[@id="activity-name"]')[0].text.replace(" ","-")
scroll_to_bottom(driver)
driver.execute_script('document.title="{}";window.print();'.format(title))
time.sleep(2)
pyautogui.moveTo(1364, 749, 1)
pyautogui.click(button='left')
driver.close()
driver.switch_to.window(driver.window_handles[0])
注意,因为保存为pdf的时候,会跳出弹窗自定义保存的位置,这个弹窗是属于系统自带的,不属于浏览器的,所以selenium是不起作用的。所以我使用了pyautogui来操控鼠标,使得鼠标移动到保存这个位置。也就是说,爬取学习文章并转化为pdf的过程中,需要保持鼠标静止,自己不能动鼠标。这样,pyautogui的这个配置才生效:pyautogui.moveTo(1364, 749, 1)。其中,参数1364与749即是保存按钮的位置。这边建议下载个微信电脑版,然后ALT+A就可以知道保存这个按钮的位置了。注意:这个1364,749只是我电脑跳出弹窗时保存按钮的位置,不同的电脑的保存弹窗的保存按钮的位置都不一样,所以这个地方必须按照自己电脑的保存弹窗的保存按钮实时更改。还有一个特殊情况就是两篇学习文章同名了,就会使得爬取的消息停止下来,我爬取过是不会同名的。如果是重新爬一次,最好把旧的pdf都删了。
6、将爬取到的pdf转为一张长图片
import fitz # fitz就是pip install PyMuPDF
import os
import glob
from PIL import Image
def pdf_to_pages_photo(pdfPath, imagePath, split_name):
pdf_context = fitz.open(pdfPath)
for pg in range(pdf_context.pageCount):
page = pdf_context[pg]
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像。
# 此处若是不做设置,默认图片大小为:792X612, dpi=96
zoom_x = 1.33333333 # (1.33333333-->1056x816) (2-->1584x1224)
zoom_y = 1.33333333
mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
pix = page.getPixmap(matrix=mat, alpha=False)
if not os.path.exists(imagePath): # 判断存放图片的文件夹是否存在
os.makedirs(imagePath) # 若图片文件夹不存在就创建
pix.writePNG(imagePath + '{}{}.png'.format(split_name,str(pg+1))) # 将图片写入指定的文件夹内
def pages_photo_APhoto(path,split_photo_name,title):
page_photo_list = []
image_list = []
page_photo_list = glob.glob('{}{}*.png'.format(path,split_photo_name))
page_photo_list = [fn.replace(path,'') for fn in page_photo_list]
# 文件夹名 按数字排序
page_photo_list.sort(key=lambda x: int(x[len(split_photo_name):-4]))
for pphoto in page_photo_list:
image_list.append(Image.open(path + os.sep + pphoto))
width = 0
height = 0
for img in image_list:
# 单幅图像尺寸
w, h = img.size
height += h
# 取最大的宽度作为拼接图的宽度
width = max(width, w)
# 创建空白长图
result = Image.new(image_list[0].mode, (width, height), 0xffffff)
# 拼接图片
height = 0
for img in image_list:
w, h = img.size
# 图片水平居中
result.paste(img, box=(round(width / 2 - w / 2), height))
height += h
# 保存图片
result.save('{}{}.png'.format(path,title),quality=95)
# 删除那些片段化的图片,因为已经合成了长图片了
for pphoto in page_photo_list:
os.remove(path+pphoto)
time.sleep(1)
# 定义图片存放的位置
imagePath = 'E:\\data\\photo\\'
# 这是刚刚爬取的pdf的下载路径
pdfPath = 'E:\\data\\pdf\\'+title+'.pdf'
#将pdf转化为图片,是按页转换
split_photo_name = 'images_'
pdf_to_pages_photo(pdfPath, imagePath, split_photo_name)
# 将按页转换的图片转化为一张长图片,并删除多余的图片
page_title = os.path.split(pdfPath)[1].replace('.pdf','')
pages_photo_APhoto(imagePath,split_photo_name,page_title)
在代码中,有两个地方需要注意:
- 第一个就是存放下载好的pdf的路径。一般是浏览器的默认下载地址,这个时候,我们就需要去浏览器的设置那里查看自己的下载路径。然后根据实际情况更换掉我的路径E:\data\pdf。
- 第二个就是存放图片的绝对路径,这个自己随便定义就行,一般我建议放在与pdf相近的地方,这样方便全选发到手机在挤地铁无聊的时候学习一下。
至此,所有的注意事项基本都说了,如果大家按照我的介绍去一步步执行,并且不移动鼠标,那么你就可以得到一个pdf以及对应的长屏图片。
7、完整代码
通过上面1-6节的介绍。如果有正确运行,通过观察selenium操作的谷歌浏览器的运行页面,相信大家已经知道整体的流程以及步骤是怎么实现的了,现在我分享一下实现的步骤以及其完整的代码。ps:可以爬取到1000篇学习文章的那种,虽然爬取1000篇可能需要几个小时。
from selenium import webdriver
import json
import time
import pyautogui
import fitz # fitz就是pip install PyMuPDF
import os
import glob
from PIL import Image
# 设置保存为pdf的参数
appState = """{
"recentDestinations": [
{
"id": "Save as PDF",
"origin": "local"
}
],
"mediaSize": {
"height_microns": 279400,
"name": "NA_LETTER",
"width_microns": 215900,
"custom_display_name": "Letter"
},
"selectedDestinationId": "Save as PDF",
"version": 2,
"isHeaderFooterEnabled": false
}"""
appState = json.loads(appState)
profile = {"printing.print_preview_sticky_settings.appState": json.dumps(appState)}
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option('prefs', profile)
# 该参数必须存在,不设置会无法使用打印,保存全屏图片等功能
chrome_options.add_argument('--kiosk-printing')
# 打开一个浏览器窗口
driver = webdriver.Chrome(options=chrome_options)
# 登录搜狗微信
driver.get("https://weixin.sogou.com/")
time.sleep(3)
driver.find_elements_by_xpath('//a[@id="loginBtn"]')[0].click()
微信扫码登录之后,更改关键字,保存按钮的pos位置,pdf的存放路径,图片的存放路径为符合自己电脑真实情况,然后静等10多分钟就可以得到1000页学习文章了。
def scroll_to_bottom(driver):
js = "return action=document.body.scrollHeight"
# 初始化现在滚动条所在高度为0
height = 0
# 当前窗口总高度
new_height = driver.execute_script(js)
while height < new_height:
# 将滚动条调整至页面底部
for i in range(height, new_height, 100):
driver.execute_script('window.scrollTo(0, {})'.format(i))
time.sleep(0.5)
height = new_height
time.sleep(2)
new_height = driver.execute_script(js)
def get_pdf(n):
for i in range(n):
for i in range(10):
driver.find_elements_by_xpath('//a[@uigs="article_title_{}"]'.format(i))[0].click()
driver.switch_to.window(driver.window_handles[1])
time.sleep(1)
title = driver.find_elements_by_xpath('//*[@id="activity-name"]')[0].text.replace(" ","-")
scroll_to_bottom(driver)
driver.execute_script('document.title="{}";window.print();'.format(title))
time.sleep(2)
pyautogui.moveTo(1364, 749, 1)
pyautogui.click(button='left')
driver.close()
driver.switch_to.window(driver.window_handles[0])
def pdf_to_pages_photo(pdfPath, imagePath, split_name):
pdf_context = fitz.open(pdfPath)
for pg in range(pdf_context.pageCount):
page = pdf_context[pg]
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像。
# 此处若是不做设置,默认图片大小为:792X612, dpi=96
zoom_x = 1.33333333 # (1.33333333-->1056x816) (2-->1584x1224)
zoom_y = 1.33333333
mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
pix = page.getPixmap(matrix=mat, alpha=False)
if not os.path.exists(imagePath): # 判断存放图片的文件夹是否存在
os.makedirs(imagePath) # 若图片文件夹不存在就创建
pix.writePNG(imagePath + '{}{}.png'.format(split_name,str(pg+1))) # 将图片写入指定的文件夹内
def pages_photo_APhoto(path,split_photo_name,title):
page_photo_list = []
image_list = []
page_photo_list = glob.glob('{}{}*.png'.format(path,split_photo_name))
page_photo_list = [fn.replace(path,'') for fn in page_photo_list]
# 文件夹名 按数字排序
page_photo_list.sort(key=lambda x: int(x[len(split_photo_name):-4]))
for pphoto in page_photo_list:
image_list.append(Image.open(path + os.sep + pphoto))
width = 0
height = 0
for img in image_list:
# 单幅图像尺寸
w, h = img.size
height += h
# 取最大的宽度作为拼接图的宽度
width = max(width, w)
# 创建空白长图
result = Image.new(image_list[0].mode, (width, height), 0xffffff)
# 拼接图片
height = 0
for img in image_list:
w, h = img.size
# 图片水平居中
result.paste(img, box=(round(width / 2 - w / 2), height))
height += h
# 保存图片
result.save('{}{}.png'.format(path,title),quality=95)
# 删除那些片段化的图片,因为已经合成了长图片了
for pphoto in page_photo_list:
os.remove(path+pphoto)
time.sleep(1)
if __name__ == "__main__":
# 1、文本框中输入你要收集的学习文章的搜索关键字“XXX”
query = driver.find_element_by_name("query")
query.send_keys("selenium")
# 2、点击搜索按钮
time.sleep(1)
driver.find_elements_by_xpath('//input[@uigs="search_article"]')[0].click()
# 3、打印了所有的目标pdf,最多100页,如果不先扫码登录,最多就只能爬取10页
get_pdf(100)
# 4、获取刚刚爬取的所有pdf的存放路径
list_pdf = glob.glob('E:\\data\\pdf\\*.pdf')
# 5、定义储存图片的目录
imagePath = 'E:\\data\\photo\\'
for pdf in list_pdf:
# 6、循环得到的每一个PDF地址
pdfPath = pdf
# 7、将pdf转化为图片,是按页转换
split_photo_name = 'images_'
pdf_to_pages_photo(pdfPath, imagePath, split_photo_name)
# 8、将按页转换的图片转化为一张长图片,并删除多余的图片
title = os.path.split(pdf)[1].replace('.pdf','')
pages_photo_APhoto(imagePath,split_photo_name,title)
创作不易,如果这篇博客有帮助到你,麻烦你帮忙点个赞加收藏再走。
|