本人是个股票爱好者,同时也是个Python学习者,最近学习Python的爬虫技术抓取自己喜欢的股票公众号的文章并保存起来,希望有缘者看到,如果你能按照我的文章成功执行,也许会有意向不到的收货。以下是我参考的链接,实际上下面我的实际操作过程。
https://mp.weixin.qq.com/s/jGjCfnGdxzK29FgC4E-_Cg
1.抓取的思路
下面三种思路我选择了第三种,第二种我也试了,失败。
1)通过手机和电脑相连,利用 Fiddler 抓包获取请求和返回报文,然后通过报文模拟请求实现批量下载。
2)通过搜狗浏览器或者用 wechatsogou 这个 Python 模块,去搜索公号后,实现批量下载。
3)通过公众号平台,这个需要你能登陆到公众号平台即可,剩下就比较简单。
2.抓取之前的准备工作
1)本机安装Python ,Python的安装方法,可以上网搜素一下,网上很多,笔者随便找了个一个。
https://www.jianshu.com/p/c389b2f2b04f
- 本人有一个公众号,如何申请公众号,也请上网搜索,笔者也搜索了一个,请参考
https://www.jianshu.com/p/a1164d48dc2c?from=groupmessage
3.言归正传,开始抓取公众号文章。
1)获取 Cookie
首先我们登陆到公众号平台,登陆成功后会跳转到公众号管理首页,如下图:
然后我们在当前页面打开浏览器开发者工具快捷键F12,刷新下页面,在网络里就能看到各种请求,在这里我们点开一个请求 url,然后就能看到下图网络请求信息,里面包含请求的 Cookie 信息。
接下来我们需要把 Cookie 信息复制下来转换成 Json 格式串保存到文本文件里,以供后面请求链接时使用。这里需要写一段 Python 代码进行处理,新建文件 gen_cookies.py 写入代码如下:
好了,将 Cookie 写入文件后,接下来就来说下在哪里可以找到某公号的文章链接。
获取文章链接
在公号管理平台首页点击左侧素材管理菜单,进入素材管理页面,然后点击右侧的新建图文素材按钮,如下图:
进入新建图文素材页面,然后点击这里的超链接:
在编辑超链接的弹出框里,点击选择其他公众号的连接:
在这里我们就能通过搜索,输入关键字搜索我们想要找到公众号,比如在这里我们搜索 "每日看懂龙头股",就能看到如下搜索结果:
然后点击第一个 每日看懂龙头股的公众号,在这里我们就能看到这个公众号历史发布过的所有文章:
我们看到这里文章每页只显示五篇,一共分了232页,现在我们再打开自带的开发者工具,然后在列表下面点下一页的按钮,在网络中会看到向服务发送了一个请求,我们分析下这个请求的参数。
通过请求参数,我们大概可以分析出参数的意义, begin 是从第几篇文章开始,count 是一次查出几篇,fakeId 对应这个公号的唯一 Id,token 是通过 Cookie 信息来获取的。好了,知道这些我们就可以用 Python 写段代码去遍历请求,新建文件 gzh_download.py,代码如下:
import datetime
import random
from pathlib import Path
from pathlib import Path
import requests
import json
import time
import re
from bs4 import BeautifulSoup
import os
from PIL import Image, ImageDraw, ImageFont
import shutil
import pdfkit
import wechatsogou
保存下载的 html 页面和图片
def save(search_response, html_dir, file_name):
# 保存 html 的位置
htmlDir = os.path.join(os.path.dirname(os.path.abspath(file)), html_dir)
# 保存图片的位置
targetDir = os.path.join(os.path.dirname(os.path.abspath(file)), html_dir + '/images')
# 不存在创建文件夹
if not os.path.isdir(targetDir):
os.makedirs(targetDir)
domain = 'https://mp.weixin.qq.com/s'
# 调用保存 html 方法
save_html(search_response, htmlDir, file_name)
# 调用保存图片方法
save_file_to_local(htmlDir, targetDir, search_response, domain, file_name)
保存图片到本地
def save_file_to_local(htmlDir, targetDir, search_response, domain, file_name):
# 使用lxml解析请求返回的页面
obj = BeautifulSoup(save_html(search_response, htmlDir, file_name).content, 'lxml')
# 找到有 img 标签的内容
imgs = obj.find_all('img')
# 将页面上图片的链接加入list
urls = []
for img in imgs:
if 'data-src' in str(img):
urls.append(img['data-src'])
elif 'src=""' in str(img):
pass
elif "src" not in str(img):
pass
else:
urls.append(img['src'])
# 遍历所有图片链接,将图片保存到本地指定文件夹,图片名字用0,1,2...
i = 0
for each_url in urls:
# 跟据文章的图片格式进行处理
if each_url.startswith('//'):
new_url = 'https:' + each_url
r_pic = requests.get(new_url)
elif each_url.startswith('/') and each_url.endswith('gif'):
new_url = domain + each_url
r_pic = requests.get(new_url)
elif each_url.endswith('png') or each_url.endswith('jpg') or each_url.endswith('gif') or each_url.endswith(
'jpeg'):
r_pic = requests.get(each_url)
time.sleep(random.randint(1, 2))
# 创建指定目录
t = os.path.join(targetDir, str(i) + '.jpeg')
# t1要把图片存成相对路径,否则移动位置无法打开
t1 = os.path.join('images', str(i) + '.jpeg')
print('该文章共需处理--' + str(len(urls)) + '张图片,正在处理第' + str(i + 1) + '张……')
# 指定绝对路径
fw = open(t, 'wb')
# 保存图片到本地指定目录
fw.write(r_pic.content)
i += 1
# 将旧的链接或相对链接修改为直接访问本地图片
update_file(each_url, t1, htmlDir, file_name)
fw.close()
# 给图片加水印
fp = open(t, 'rb')
im = Image.open(fp).convert('RGBA') # 这里改为文件句柄
barcode = random.choice([r'F:\File\个人\微信二维码.png', r'F:\File\个人\微信二维码.png'])
logo = Image.open(barcode)
txt = Image.new('RGBA', im.size, (0, 0, 0, 0))
txt.paste(logo, (im.size[0] - logo.size[0], im.size[1] - logo.size[1]))
fnt = ImageFont.truetype("c:/Windows/fonts/simkai.ttf", 20)
d = ImageDraw.Draw(txt)
d.text((im.size[0] - 250, im.size[1] - 80), u"WX:guduxin2020 整理", font=fnt, fill=(255, 255, 255, 255))
Image.alpha_composite(im, txt).save(str(t).replace('jpeg', 'png'), 'png')
fp.close()
shutil.copyfile(str(t).replace('jpeg', 'png'), t)
os.remove(str(t).replace('jpeg', 'png'))
保存 HTML 到本地
def save_html(url_content, htmlDir, file_name):
f = open(htmlDir + "/" + file_name + '.html', 'wb')
# 写入文件
f.write(url_content.content)
f.close()
return url_content
修改 HTML 文件,将图片的路径改为本地的路径
def update_file(old, new, htmlDir, file_name):
# 打开两个文件,原始文件用来读,另一个文件将修改的内容写入
with open(htmlDir + "/" + file_name + '.html', encoding='utf-8') as f, open(htmlDir + "/" + file_name + '_bak.html',
'w', encoding='utf-8') as fw:
# 遍历每行,用replace()方法替换路径
for line in f:
new_line = line.replace(old, new)
new_line = new_line.replace("data-src", "src")
# 写入新文件
fw.write(new_line)
# 执行完,删除原始文件
os.remove(htmlDir + "/" + file_name + '.html')
time.sleep(0.5)
# 修改新文件名为 html
os.rename(htmlDir + "/" + file_name + '_bak.html', htmlDir + "/" + file_name + '.html')
with open("F:\source\PYTHON\python-100-day-master\python-100-day-master\公众号文章保存\cookie.txt", "r") as file:
cookie = file.read()
cookies = json.loads(cookie)
url = "https://mp.weixin.qq.com"
response = requests.get(url, cookies=cookies)
token = re.findall(r'token=(\d+)', str(response.url))[0]
token = re.findall(r'token=(\d+)', str(response.url))[0]
headers = {
"Host": "mp.weixin.qq.com",
"Referer": "https://mp.weixin.qq.com/cgi-bin/appmsg?t=media/appmsg_edit_v2&action=edit&isNew=1&type=10&token=1595906960&lang=zh_CN",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
}
循环遍历前10页的文章
for j in range(1, 5, 1):
begin = (j - 1) * 5
# 请求当前页获取文章列表
requestUrl = "https://mp.weixin.qq.com/cgi-bin/appmsg?action=list_ex&begin=" + str(
begin) + "&count=5&fakeid=MzAxMDYxNDQ4NA==&type=9&query=&token=" + token + "&lang=zh_CN&f=json&ajax=1"
search_response = requests.get(requestUrl, cookies=cookies, headers=headers)
# MzU2NzEwMDc1MA== 爱在冰川
# MzAxMDYxNDQ4NA== 看懂龙头股
# MzIyMjAyMzkyNw==下注的快感
# Mzg5MzEyNzEwNQ==财联社
# MzI5ODc4Njc2Ng==盘口逻辑拆解
# 获取到返回列表 Json 信息
re_text = search_response.json()
list = re_text.get("app_msg_list")
if list is not None:
# file_normal = open('公众号文章链接_平常1.txt', mode='a', encoding='utf-8')
# file_saturday = open('公众号文章链接_周六1.txt', mode='a', encoding='utf-8')
# 遍历当前页的文章列表
for i in list:
# 将文章链接转换 pdf 下载到当前目录
tupTime = time.localtime(i["create_time"]) # 秒时间戳
standardTime = time.strftime("%Y%m%d", tupTime) # 转换Wie正常的时间
title = i["title"]
# print(i["link"] + " " + i["title"] + " " + standardTime)
# 目录名为标题名,目录下存放 html 和图片
pat = r"(\xe2\x98\x85|\xe2\x97\x86)|(\[\d+-\d+-\d+\])"
repat = re.compile(pat)
# 特殊字符替换
dir_name = repat.sub('_', i["title"])
print(os.path.dirname(__file__))
# if (os.path.exists(dir_name)):
# print('已经存在' + dir_name)
# continue
dir_name = dir_name.replace(' ', '')
dir_name = dir_name.replace(',', '')
dir_name = dir_name.replace(':', '')
dir_name = dir_name.replace('?', '')
dir_name = dir_name.replace('?', '')
dir_name = dir_name.replace('/', '_')
dir_name = dir_name.replace('|', '_')
dir_name = dir_name.replace('*', '_')
# dir_name = i["title"].replace(' ', '').replace(':',)
myfolder = Path(standardTime + '_' + dir_name)
if myfolder.is_dir():
print('已经存在' + dir_name)
continue
print("正在下载文章:" + dir_name)
# 请求文章的url,获取文章内容
response = requests.get(i["link"], cookies=cookies, headers=headers)
# 保存文章到本地
try:
if str(i["title"]).__contains__('周六'):
save(response, standardTime + '_' + dir_name,standardTime + '_' + dir_name)
else:
save(response, standardTime + '_' + dir_name, standardTime + '_' + dir_name)
except Exception:
# print('第d%页第d%篇文章错误' % (j - 1) % begin)
print(('第{page}页第{count}篇文章{dir_name}错误' + str(Exception.args)).format(page=(j - 1), count=(begin),
dir_name=dir_name))
continue
# 将html文件转换为pdf文件
# pdfkit.from_file(dir_name + "/" + i["aid"] + '.html', i["title"] + standardTime + '.pdf')
# 重命名pdf文件名字
# os.rename(dir_name + "/" + standardTime + '.html', dir_name + "/" + dir_name+standardTime + '.html')
print('第{page}页第{count}篇文章{dir_name} 下载完成!'.format(page=str(j - 1), count=str(begin), dir_name=dir_name))
# if str(i["title"]).__contains__('周六'):
# file_saturday.writelines(i["title"] + " " + standardTime + "\r\n' " + i["link"] + '\r\n')
# else:
# file_normal.writelines(i["title"] + " " + standardTime + "\r\n' " + i["link"] + '\r\n')
# 过快请求可能会被微信问候,这里进行10秒等待
# time.sleep(1)
# file_normal.close()
# file_saturday.close()
本人的代码里还包含了一些自己很喜欢的公众号的代码,另外本人代码里还集成了下载的公众号文章的图片加水印的功能,防止别的人拿去涉及到版权问题。
总结
本文为大家介绍了如何通过分析公众号平台的功能,找到可以访问到每日看懂龙头股所有文章的链接,从而可以批量下载该公众号所有文章,由于下载并保存成pdf文件会出现错误,本人改成了使用html的形式,如果有兴趣你也可以试试。
源代码参考:
https://github.com/pitucre/gongzhonghao.git
相关免费文章我已经整理并放到网盘上了,大家可以去下载、
链接:https://pan.baidu.com/s/1dz7CMvSsgYvh6TytXZcStQ
提取码:sdbo
如果有任何疑问也可以加我qq:282104333 ,或者微信:guduxin2020