微信公众号文章爬虫尝试

需求

之所以想要抓取微信公众号的文章,是因为我想把孟岩公众号的文章打包整理为一个电子书,方便查看,统一阅读。微信公众号对于历史文章支持的很不好。卡片式的展示也很影响连续阅读的体验。再吐槽一下,公众号的banner风尚也是一个很鸡肋的存在,很是累赘。

失败的尝试

  1. 首先根据网上的方法利用Fiddler获取文章列表,但是没有成功,以失败告终
  2. 使用文档导出助手,导出的文章是独立的pdf格式,epub格式需要客服手工处理,质量也不好保证

微信代理

很多人可能不知道,微信是可以挂代理的。TG、TIM、QQ好像都是差不多的设计,差不多是IM工具标配了。

一个半自动爬取文章内容的方法

1. 手工抓取公众号文章链接

微信的反爬机制做的很好(后来我细想可能也不单是为了反爬),最终我也没找到可以批量获取公众号链接的方法。

网上查了很多资料,做了很多尝试,想自动抓取公众号的全部链接都没有成功,这个路堵得很死。这样我就利用手工点击的方式抓去了孟岩公众号的所有链接。好在公众号只有两百多篇文章,半个多小时也就手工抓完了。

2. Selenium爬取公众号HTML原码

最初尝试使用requests,无法爬取到文章内容。做了一些尝试后发现姿势不对。然后换用selenium爬取。立杆见影,成功了。但是又遇到了一个新问题,图片不能加载。这样在selenium中开始尝试自动化控制滚轮,强制刷新图片,这样可以了。

代码如下:

import os
import sys
from selenium import webdriver
from selenium.webdriver.common.keys import Keys 
from selenium.webdriver.common.action_chains import ActionChains 

driver = webdriver.Chrome()
#driver.set_window_size(1080,800)

action = ActionChains(driver)

def is_end():
    pageHeight = driver.execute_script("return document.body.scrollHeight")
    totalScrolledHeight = driver.execute_script("return window.pageYOffset + window.innerHeight")
    # -1 is to make sure the rounding issues
    print(pageHeight, totalScrolledHeight)
    if((pageHeight-1)<=totalScrolledHeight):
        return True
    else:
        return False

def scroll_down(cnt):
    for i in range(cnt):
        time.sleep(1)
        # action.send_keys(Keys.PAGE_DOWN)
        # action.perform()
        driver.execute_script("window.scrollBy(0,500)")

        if is_end():
            print("it is end")
            time.sleep(2)
            break
        else:
            print("not end")

with open(sys.argv[1], 'r', encoding='utf-8') as fp:
    for cnt, line in enumerate(fp):
        #print("Line {}: {}".format(cnt, line.strip()))
        driver.get(url=line)

        time.sleep(0.5)
        scroll_down(20)
        time.sleep(0.5)

        print(type(driver.title), driver.title)

        with open("%03d.html"%cnt, 'w', encoding='utf-8') as f:
            print(driver.page_source, file=f)

        #break
        # if cnt > 1:
        #     break

driver.close()

代码解释:

  • 需要传递一个文件参数(sys.argv[1]),文件存储的内容为链接
  • 要想运行需要安装本地安装chrome浏览器,并安装chromedriver(全局目录)
  • 延时参数需要根据实际网络状况调整(不着急可以把全部参数加倍)

总结

  • python大法好
  • 爬取到html之后可以再利用BS4进行抽取格式化
  • 然后利用pandoc将html转为md格式并把图片下载到本地(用scapy代替pandoc应该也是可以的,pandoc更简单)
  • 微信的html源码里面有不少注释存在,这个还是让我有些意外的,大概这个地方不用做的极致吧
  • 如果可以快速获取公众号全部链接接,整个工具链就完备了(欢迎各位读者留言告知)