使用Puppeteer爬取微信文章的实现


Posted in Python onFebruary 11, 2020

一朋友在群里问有没有什么办法能够一次性把这个链接 里的文章保存下来。点开可以看到,其实就是一个文章合集。所以需求就是,把这个文档中的链接里的文章挨个保存下来。保存形式可以有很多种,可以是图片,也可以是网页。这里因为使用 puppeteer 库的原因,故选择保存格式格式为PDF。

需求解构

完成整个动作,主要分为这两个部分。获取文档内所有文章的链接;把每个链接里的内容保存为PDF文件。

对于获取链接,有两条路,一是使用request模块请求该网址获取文档;二是把网页保存到本地使用fs模块获取文档内容。拿到文档也就是整个HTML文档后,一开始没想到什么好法子来拿到全部文章链接。如果直接在网页那就好办,直接DOM的 quertSelectorAll API配合CSS选择器就可以非常方便地拿到所有 a 链接中的 href 属性。但这里是Node,是DOM外之地。又想到的是直接使用正则匹配,后来还是放弃了这个做法。在google搜了下才发现竟然忘了 cheerio 这个好东西。 cheerio 是一个专门为服务端设计的快速灵活而简洁得jQuery实现。

对于保存网页内容,我所知道的常规操作是保存为PDF文件,恰巧之前刚知道的 puppeteer 满足这样的需求。 puppeteer 是一个由 chrome devtools 团队维护的提供了控制chrome浏览器高级API的一个Node库。除去爬取网页内容保存为PDF文件外,它还可以作为服务端渲染的一个方案以及实现自动化测试的一个方案。

需求实现

获取链接

先上这部分代码

const getHref = function () {
 let file = fs.readFileSync('./index.html').toString()
 const $ = cheerio.load(file)
 let hrefs = $('#sam').find('a')
 for (e in hrefs) {
  if (hrefs[e].attribs && hrefs[e].attribs['href']) {
   hrefArr.push({
    index: e,
    href: hrefs[e].attribs['href']
   })
  }
 }
 fs.writeFileSync('hrefJson.json', JSON.stringify(hrefArr))
}

因为后面的代码都依赖到读取的文件,所以这里用的是readFileSync方法。如果没有声明返回内容的格式,那默认是Buffer格式。可以选择填写 utf8 格式,或者直接在该方法后面使用 toString 方法。

两行代码用cheerio拿到所有所有链接的DOM元素后,挨个将其处理为方便后面要用到的格式。考虑到可能存在a标签没有href属性的情况,这里还对其进行了判断,不过这也是后面调试程序时才发现的bug。

如果需要将所有的链接另外保存起来,使用 writeFile 方法。

存为PDF

同样,先上这部分代码。

const saveToPdf = function () {
 async () => {
  const browser = await puppeteer.launch({
   executablePath: './chrome-win/chrome.exe',
  });

  // 链接计数
  let i = 0

  async function getPage() {
   const page = await browser.newPage();
   await page.goto(hrefArr[i]['href'], { waitUntil: 'domcontentloaded' });

   // 网页标题
   let pageTitle

   if (hrefArr[i]['href'].includes('weixin')) {
    pageTitle = await page.$eval('meta[property="og:title"]', el => el.content)
   } else {
    pageTitle = await page.$eval('title', el => el.innerHTML)
   }

   let title = pageTitle.trim()
   // 去掉斜杆
   let titlea = title.replace(/\s*/g, "")
   // 去掉竖线
   let titleb = titlea.replace(/\|/g, "");
   
   await page.pdf({ path: `${i}${titleb}.pdf` });

   i++

   if (i < hrefArr.length) {
    getPage()
   } else {
    await browser.close();
   }
  }
  getPage()
 }
}

因为需要等待chrome浏览器的打开,以及其他可能的异步请求。最外层使用了async 配合箭头函数将真正的执行代码包住。

在用 npm 安装 puppetter 时,因为默认会下载chrome浏览器,而服务器在国外,一般都无法下载成功。当然也有相应的解决方案,这里我就不展开了。如果安装 puppeteer ,可以参开 这篇文章 或者直接谷歌搜下。

在前一部分说到,我们需要把不止一个链接里的内容保存为PDF,所以使用了变量 i 来标识每一次需要访问的链接。

对于获取网页标题,当时确实费了点时间才处理好拿到已有链接的网页标题。所以链接中主要有两种网站的链接,一类是微信公众号文章,另一类是新浪财新这种网站。微信文章里头没有像新浪这样直接给出 title 内容。

使用Puppeteer爬取微信文章的实现 

使用Puppeteer爬取微信文章的实现

这个时候就要用到 page 类中的 $eval 方法, $eval 方法主要有两个参数,一是选择器,二是在浏览器上下文中执行的函数。$eval方法会页面中运行document.querySelector方法,并将其返回值传递给第二个参数,也就是我们写好的方法中。以获取新浪网页文章title为例, title 为传入选择器,我们需要的是其标签内容。

pageTitle = await page.$eval('title', el => el.innerHTML)

在产生文件名的过程中,由于文件夹还是文件路径的一部分。此时还需要考虑到windows文件路径规范。但网页中的标题并不受此规范限制,由此产生矛盾。这个问题也是后面调试的时候才发现,一开始写代码并没有想到这个问题。即需要去除标题中的斜杠竖杆还有空格等字符。

每获取完一个链接的内容后,就将链接位置标识 i + 1,知道所有链接内容保存完毕,关闭打开的网页。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python Web框架Flask中使用七牛云存储实例
Feb 08 Python
python实现可以断点续传和并发的ftp程序
Sep 13 Python
CentOS 6.X系统下升级Python2.6到Python2.7 的方法
Oct 12 Python
Python正则替换字符串函数re.sub用法示例
Jan 19 Python
Python计时相关操作详解【time,datetime】
May 26 Python
python输入错误密码用户锁定实现方法
Nov 27 Python
Python实现KNN邻近算法
Jan 28 Python
关于python之字典的嵌套,递归调用方法
Jan 21 Python
python统计文章中单词出现次数实例
Feb 27 Python
VScode连接远程服务器上的jupyter notebook的实现
Apr 23 Python
解决Keras TensorFlow 混编中 trainable=False设置无效问题
Jun 28 Python
python某漫画app逆向
Mar 31 Python
Python实现遗传算法(二进制编码)求函数最优值方式
Feb 11 #Python
python加密解密库cryptography使用openSSL生成的密匙加密解密
Feb 11 #Python
如何通过python实现全排列
Feb 11 #Python
Python3加密解密库Crypto的RSA加解密和签名/验签实现方法实例
Feb 11 #Python
python 遗传算法求函数极值的实现代码
Feb 11 #Python
在django中使用apscheduler 执行计划任务的实现方法
Feb 11 #Python
django在保存图像的同时压缩图像示例代码详解
Feb 11 #Python
You might like
APMServ使用说明
2006/10/23 PHP
php 服务器调试 Zend Debugger 的安装教程
2009/09/25 PHP
PHP 的ArrayAccess接口 像数组一样来访问你的PHP对象
2010/10/12 PHP
Php图像处理类代码分享
2012/01/19 PHP
CodeIgniter控制器之业务逻辑实例分析
2016/01/20 PHP
php生成图片缩略图功能示例
2017/02/22 PHP
让GoogleCode的SVN下的HTML文件在FireFox下正常显示.
2009/05/25 Javascript
让IE6支持min-width和max-width的方法
2010/06/25 Javascript
转换json格式的日期为Javascript对象的函数
2010/07/13 Javascript
jQuery :nth-child前有无空格的区别分析
2011/07/11 Javascript
jQuery EasyUI API 中文文档 - ComboGrid 组合表格
2011/10/13 Javascript
ajax上传时参数提交不更新等相关问题
2012/12/11 Javascript
js仿百度有啊通栏展示效果实现代码
2013/05/28 Javascript
jQuery之选项卡的简单实现
2014/02/28 Javascript
js加密解密字符串可自定义密码因子
2014/05/13 Javascript
新手快速学习JavaScript免费教程资源汇总
2015/06/25 Javascript
谈谈impress.js初步理解
2015/09/09 Javascript
简单的jQuery banner图片轮播实例代码
2016/03/04 Javascript
JS实现鼠标滑过显示边框的菜单效果
2016/09/21 Javascript
在vue.js中抽出公共代码的方法示例
2017/06/08 Javascript
vue中如何创建多个ueditor实例教程
2017/11/14 Javascript
通过一次报错详细谈谈Point事件
2018/05/17 Javascript
浅谈微信JS-SDK 微信分享接口开发(介绍版)
2018/08/15 Javascript
Python实现遍历windows所有窗口并输出窗口标题的方法
2015/03/13 Python
python通过函数属性实现全局变量的方法
2015/05/16 Python
Python自动化完成tb喵币任务的操作方法
2019/10/30 Python
Django用数据库表反向生成models类知识点详解
2020/03/25 Python
python环境搭建和pycharm的安装配置及汉化详细教程(零基础小白版)
2020/08/19 Python
CSS3关于z-index不生效问题的解决
2020/02/19 HTML / CSS
瑜伽国际:Yoga International
2018/04/18 全球购物
成人高等教育毕业生自我鉴定
2013/10/22 职场文书
护士实习自荐信
2015/03/06 职场文书
学校党员干部承诺书
2015/05/04 职场文书
2016关于学习党章的心得体会
2016/01/15 职场文书
2019下半年英语教师的教学工作计划(3篇)
2019/09/25 职场文书
MySQL利用UNION连接2个查询排序失效详解
2021/11/20 MySQL