爬虫之微信公众号
2019-02-27 本文已影响161人
7e3c6097c856
wechat_official_account.png
与自小一起玩的一小伙伴,昨晚聊天,说他导师需要这个公众号爆炸实验室的文章,有没有兴致。刚好有兴趣,就试一试。
总体思路是:
- 获取文章链接及对应标题;
- 将链接写入文本文档或存入数据库;
- 单读解析每一个网址并下载至本地。
下文按照这三个步骤逐次展开。
获取文章链接
我采取的是以下两种方法:
- 请参考这个项目;
- 查找公众号有没有推送历史文章的集合,假如有,则从该网页采集各个文章链接。
将链接写入文本文档
- 解析原作推送的历史记录获得每一篇文章的连接及标题;
- 解析后得到类似这样的数据,共计681条:
6个魔力十足的弹性科学小实验 http://mp.weixin.qq.com/s?__biz=MzU5MDI4OTE3OQ==&mid=2247495513&idx=1&sn=1c52524870c2643ad4940a38437b5fc6&chksm=fdc22f2dcab5a63b4e9f39e1156f83acfcb7814fa9c01f55daa89bc678aa451da9486cd68b6c&scene=21#wechat_redirect
解析网页
# coding:utf-8
"""
0. Win 10 教育版 + Python 3.7.0
1. 我的Chrome是 Version 72.0.3626.119 (Official Build) (32-bit);
2. 你需要安装selenium包:pip install selenium;
3. 你需要安装BeautifulSoup包: pip install beautifulsoup4;
4. 你需要将驱动文件 chromedriver.exe 放入环境变量,该文件在selenium官网找一下,不同系统用不一样的驱动
"""
from selenium import webdriver
from bs4 import BeautifulSoup
import win32api
import win32con
import random
import time
import os
def get_all_link(file='index.html'):
"""
从index.html提取每篇文章链接及标题,然后保存至bomblab.txt
:param file:
:return:
"""
with open(file, 'r+', encoding='utf-8') as file:
soup = BeautifulSoup(file, 'lxml')
for i in soup.find_all('a', target="_blank"):
print(i.text.strip() + '\t' + i['href'], file=open('bomblab.txt', 'a+', encoding='utf-8'))
# print(i['href'])
# print(i.text.strip().strip('\n'))
def download_single_spage(url):
"""
读取bomblab.txt中文章链接,然后下载并保存该文章至本地。默认为Chrome默认保存地址。
:param url:
:return:
"""
# 打开另存为mhtml功能
options = webdriver.ChromeOptions()
options.add_argument('--save-page-as-mhtml')
# 设置chromedriver,并打开webdriver
driver = webdriver.Chrome(options=options)
driver.get(url)
# 获取文章标题,最后保存文件时使用该标题命名
page_title = driver.find_element_by_tag_name('h2').text.strip().strip('\n\n').strip('\n')
page_title = page_title.replace('?', "").replace('?', '').replace('.', '').replace('。', '')
# ------------------------------------------------------------------------------------------------------------------
# 以下为模拟键盘操作
# 若网速较差时可以增加外循环次数,页面较长时增加内循环次数
for quququ in range(2):
print(quququ)
for i in range(25):
win32api.keybd_event(0x22, 0, 0, 0) # 按下PAGE DOWN key
win32api.keybd_event(0x22, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放PAGE DOWN
print('\tPAGEDOWN' + str(i))
time.sleep(random.random())
print('\n')
for i in range(25):
win32api.keybd_event(0x21, 0, 0, 0) # 按下PAGE UP key
win32api.keybd_event(0x21, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放PAGE UP
print('\tPAGEUP' + str(i))
time.sleep(random.random())
# 全选
win32api.keybd_event(17, 0, 0, 0) # 按下ctrl
win32api.keybd_event(65, 0, 0, 0) # 按下a
win32api.keybd_event(65, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放a
win32api.keybd_event(17, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放ctrl
# 保存
win32api.keybd_event(0x11, 0, 0, 0) # 按下ctrl
win32api.keybd_event(83, 0, 0, 0) # 按下s
win32api.keybd_event(83, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放s
win32api.keybd_event(17, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放ctrl
time.sleep(random.randint(2, 4) / 10)
# 切换输入法
win32api.keybd_event(0x10, 0, 0, 0) # 按下shift
win32api.keybd_event(0x10, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放shift
time.sleep(random.randint(2, 4) / 10)
print('切换输入法,休息0.5秒')
time.sleep(random.randint(1, 4) / 10)
# 输入文件名 wj.mhtml
win32api.keybd_event(0x57, 0, 0, 0) # 按下w
win32api.keybd_event(0x57, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放w
time.sleep(random.randint(2, 4) / 10)
win32api.keybd_event(0x4A, 0, 0, 0) # 按下j
win32api.keybd_event(0x4A, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放j
time.sleep(random.randint(2, 4) / 10)
win32api.keybd_event(0xBE, 0, 0, 0) # 按下英文句号
win32api.keybd_event(0xBE, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放英文句号
time.sleep(random.randint(2, 4) / 10)
win32api.keybd_event(0x4D, 0, 0, 0) # 按下m
win32api.keybd_event(0x4D, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放m
time.sleep(random.randint(2, 4) / 10)
win32api.keybd_event(0x48, 0, 0, 0) # 按下h
win32api.keybd_event(0x48, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放h
time.sleep(random.randint(2, 4) / 10)
win32api.keybd_event(0x54, 0, 0, 0) # 按下t
win32api.keybd_event(0x54, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放t
time.sleep(random.randint(2, 4) / 10)
win32api.keybd_event(0x4D, 0, 0, 0) # 按下m
win32api.keybd_event(0x4D, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放m
time.sleep(random.randint(2, 4) / 10)
win32api.keybd_event(0x4C, 0, 0, 0) # 按下l
win32api.keybd_event(0x4C, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放l
time.sleep(random.randint(2, 4) / 10)
# 切换输入法
win32api.keybd_event(0x10, 0, 0, 0) # 按下shift
win32api.keybd_event(0x10, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放shift
time.sleep(random.randint(2, 4) / 10)
# alt + s 保存
win32api.keybd_event(0x12, 0, 0, 0) # 按下shift
win32api.keybd_event(83, 0, 0, 0) # 按下s
time.sleep(random.randint(2, 4) / 10)
win32api.keybd_event(83, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放s
win32api.keybd_event(0x12, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放shift
time.sleep(random.randint(2, 4) / 10)
# 模拟操作结束
# ------------------------------------------------------------------------------------------------------------------
# # 预估保存页面弹出时间,后期根据实际网速调整
# time.sleep(10)
# win32api.keybd_event(13, 0, 0, 0) # 按下enter
# win32api.keybd_event(13, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放enter
# # 预估下载时间,后期根据实际网速调整
inner_sleep_time = random.randint(5, 20)
print('读取页面时休息,大约休息%ss,建议您将该时间修改为正常阅读一篇文章所需时间。' % inner_sleep_time)
time.sleep(inner_sleep_time + random.random())
# 关闭webdriver
driver.close()
# #old_add为我的电脑默认下载地址,这个地址需要根据具体情况修改
# old_add = r'C:\Users\Administrator\Downloads\wj.mhtml'
os.rename(r'C:\Users\Administrator\Downloads\wj.mhtml', r'C:\Users\Administrator\Downloads\%s.mhtml' % page_title)
def start_download():
with open('bomblab.txt', 'r', encoding='utf-8') as file:
link_lists = file.readlines()
# print(link_lists)
# print(len(link_lists))
for index, item in enumerate(link_lists):
try:
print(index, item)
if index % 10 == 0 and index != 0:
sleep_time = random.randint(120, 600)
print('大休息,时间是' + str(sleep_time) + '秒!')
time.sleep(random.random() + sleep_time)
elif index % random.randint(2, 30) == 0:
sleep_time = random.randint(2, 10)
print('小休息,时间是' + str(sleep_time) + '秒!')
time.sleep(random.random() + sleep_time)
else:
pass
link = item.split('\t')[1]
# title = item.split('\t')[0]
download_single_spage(link)
except:
with open('error.txt', 'a+', encoding='utf-8') as file:
file.write(str(index))
if __name__ == '__main__':
get_all_link()
start_download()
最后获得部分结果如下:
总结与回顾
- 本例还不太完整,未能全部获取;
- 缺少自动化、适应性差;
- 爬虫效率太低,应增加多进程以及分布式;
- 从开始动手到出结果总共耗时大概5小时;
- 恭喜小伙伴进入帕多瓦大学读博;
- 意料之外又是意料之中。