接着上一篇没完成的爬虫工程,继续更新最终的代码片段
最近一直在忙没时间更新文章的下一篇,正好这几天有时间,把代码重新调整了一下,更新了里面的细节,在调整代码中发现了许多问题,主要一个就是ip代理的质量不行,哪里不行呢,往下看就知道了。
三、获取每篇文章的阅读量,点赞量
想要获取文章的阅读量,在微信公众平台里面直接点击,是获取不了文章的阅读量的,测试如下:
然后我们可以去fiddler里面查看这篇文章的包,我们可以看出这个包就是
文章的内容,从body的大小可以判断出来,里面就是文章的内容。
但是我们无法从这条路由中获取到文章的阅读量,因为这条请求是一个get请求,想要获取文章的阅读点赞,首先请求得是post请求,其次需要携带三个重要的参数pass_ticket,appmsg_token,phoneCookie,想要获取这三个参数,我们就得把某个公众号里面的文章放到微信里面点击,然后再从fiddler里面查看抓包情况。
让后我们去fiddler看一下抓包情况:我们可以看到,同样有上有一个get请求,但下面多了一个post请求的内容,再看携带的参数有我们要的三个获取阅读量的重要参数,还有通过response的内容可以看出,已经能够获取文章的阅读量信息了。下面我们就可以拼接参数发送请求了。
#获取文章阅读量
def get_readNum(link,user_agent):
pass_ticket ="N/Sd9In6UXfiRSmhdRi+kRX2ZUn9HC5+4aAeb6YksHOWNLyV3VK48YZQY6oWK0/U"
appmsg_token = "1073_N8SQ6BkIGIQRZvII-hnp11Whcg8iqFcaN4Rd19rKluJDPVMDagdss_Rwbb-fI4WaoXLyxA244qF3iAp_"
# phoneCookie有效时间在两个小时左右,需要手动更新
phoneCookie = "rewardsn=; wxtokenkey=777; wxuin=2890533338; devicetype=Windows10x64; version=62090529; lang=zh_CN; pass_ticket=N/Sd9In6UXfiRSmhdRi+kRX2ZUn9HC5+4aAeb6YksHOWNLyV3VK48YZQY6oWK0/U; wap_sid2=CNqTqOIKElxWcHFLdDFkanBJZjlZbzdXVmVrejNuVXdUb3hBSERDTTBDcHlSdVAxTTFIeEpwQmdrWnM1TWRFdWtuRUlSRDFnUzRHNkNQZFpVMXl1UEVYalgyX1ljakVFQUFBfjCT5bP5BTgNQAE="
mid = link.split("&")[1].split("=")[1]
idx = link.split("&")[2].split("=")[1]
sn = link.split("&")[3].split("=")[1]
_biz = link.split("&")[0].split("_biz=")[1]
url = "http://mp.weixin.qq.com/mp/getappmsgext"
headers = {
"Cookie": phoneCookie,
"User-Agent":user_agent
}
data = {
"is_only_read": "1",
"is_temp_url": "0",
"appmsg_type": "9",
'reward_uin_count': '0'
}
params = {
"__biz": _biz,
"mid": mid,
"sn": sn,
"idx": idx,
"key": '777',
"pass_ticket": pass_ticket,
"appmsg_token": appmsg_token,
"uin": '777',
"wxtoken": "777"
}
success = False
a=1
while not success:
ip_num = ranDom_ip()[0] # 使用不同的ip进行阅读、点赞量的获取
try:
print("获取阅读量使用ip:%s"%ip_num)
content = requests.post(url, headers=headers, data=data, params=params, proxies=ip_num,timeout=6.6)#设置超时时间5秒
time.sleep(4)
content = content.json()
print(link)#文章链接
print(content)#文章内容
if 'appmsgstat' in content:
readNum = content["appmsgstat"]["read_num"]
like_num=content["appmsgstat"]["old_like_num"] #点赞量
zai_kan=content["appmsgstat"]["like_num"] #在看量
else:
readNum = 0
like_num = 0
success=True
return readNum, like_num
except:
print("获取阅读量ip出现问题,更换ip进入第二次循环获取!!!")
a+=1
# if a%5==0:
content = requests.post(url, headers=headers, data=data, params=params,timeout=5)
time.sleep(3)
content = content.json()
print(link) #文章链接
print(content)#文章内容
if 'appmsgstat' in content:
readNum = content["appmsgstat"]["read_num"]
like_num = content["appmsgstat"]["old_like_num"] # 点赞量
zai_kan = content["appmsgstat"]["like_num"] # 在看量
else:
print('文章阅读点赞获取失败!')
readNum=0
like_num=0
# else:
# continue
return readNum, like_num
需要注意的是,这三个重要的参数,其中phoneCookie是有时效性的,需要手动更新这是最麻烦的就跟cookies一样。我们通过传进去的文章链接 就可以将链接中一些有用的参数抽取出来,用来后面拼接post请求,本想做个如果报错不断循环的,但我发现我的ip池ip的质量有点不行,所以代码略有点冗余(有待改进)。
四、使用UA代理、IP代理,设置每篇文章的爬取速度
通过上面的一顿操作,我们基本就可以把文章的链接,标题,链接,都爬取下来了,完成了初步的需求:
但正在我兴高采烈的爬取的时候,发现没多久就被反爬发现了,直接就将我公众号的请求给屏蔽了,
这时候我想到了ip代理,识别我们是否是反爬,主要从ip和UA这两个, 所以后面我做了个ip代理池,从某些ip网站爬取ip下来,通过代码赛选出有用的ip组成一个ip池。贡献一个我爬ip的代码,有兴趣的小伙伴可以直接运行试试:
import requests
from bs4 import BeautifulSoup
import json
import random
import re
ip_num=[]
url="http://www.66ip.cn/1.html"
headers={
"Host": "www.66ip.cn ",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Referer": "http://www.66ip.cn/index.html",
"Cookie": "Hm_lvt_1761fabf3c988e7f04bec51acd4073f4=1595575058,1595816310; Hm_lpvt_1761fabf3c988e7f04bec51acd4073f4=1595832351",
}
content=requests.get(url,headers=headers)
content.encoding="gbk"
html=BeautifulSoup(content.text,"html.parser")
# print(html)
content=html.find_all("table",{"border":"2px"})
ip=re.findall('<td>(\d{1,4}.\d{1,4}.\d{1,4}.\d{1,4})</td>',str(content))
port=re.findall('<td>(\d{1,5})</td>',str(content))
for i in range(len(port)):
ip_num.append(ip[i]+":"+port[i])
proxies = [{'http':ip_num[num]} for num in range(len(ip_num))]
for i in range(0, len(ip_num)):
proxie = random.choice(proxies)
print("%s:当前使用的ip是:%s" % (i, proxie['http']))
try:
response = requests.get("http://mp.weixin.qq.com/", proxies=proxie, timeout=3)
print(response)
if response.status_code == 200:
# with open('ip.txt', "a", newline="")as f:
# f.write(proxie['http'] + "\n")
print("保存成功")
except Exception as e:
print("ip不可用")
我会将网站的ip筛选出来,保存到本地,再通过写一个函数,组建一个ip地址:
#代理池函数
def ranDom_ip():
with open('ip.txt',"r")as f:
ip_num=f.read()
ip_list=ip_num.split("\n")
num=random.randint(0,len(ip_list)-1)
proxies = [{'http': ip_list[num]}]
return proxies
然后在函数循环的地方调用这个ip代理的函数,就可以不断的更新你使用的ip,下面给大家列一个流程图,我们就可以知道,可以在哪里使用ip代理的函数了
通过上面这三个地方加进我们使用的ip池,不断变化的ip是成功绕过反爬的一点,下面我们不断更换ip我感觉还是有点不保障,我觉得UA也需要不断的更新,所以我导入了一个ua 的代理池
from fake_useragent import UserAgent #导入UA代理池
user_agent=UserAgent()
但是需要注意的是,我们不能将headers里面的UA全部换了,我们只能一部分加入UA代理,因为公众号的请求是由两部分组成的,我们只能加到前半部分。
就像下面这个的请求头,我们的Ua代理只能替换前半部分,这样就可以做到Ua也是不断变化的,变化的位置就跟上面ip函数的位置一致,我们就可以做到ip,UA在每一次请求之后都是不同的。
headers={
"Host":"mp.weixin.qq.com",
"User-Agent":user_agent.random +"(KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2469 MMWEBSDK/200601 Mobile Safari/537.36 MMWEBID/3809 MicroMessenger/7.0.16.1680(0x27001033) Process/toolsmp WeChat/arm64 NetType/WIFI Language/zh_CN ABI/arm64"
}
这两个参数之后我们基本可以控制不被反爬发现,但是还有重要的一个点,就是控制爬取的速度,因为ip和ua是不断变化了,但是你公众号的cookie是不能改变的,如果爬取的速度过快,它会识别到你某个公众号访问一个接口的频次过快,直接把你公众号搜文章的接口禁了,所以我们需要做个请求延迟,那我们在流程的哪里做比较好呢?可以在流程图标注的位置做延迟。
这样既能控制速度,又能让本机ip和ua不被发现,美滋滋,不足的是没有找到一个高质量的ip池,我用的都是免费的ip造成换ip过程中许多都是不能获取到阅读量的(来自穷人的感叹),如果有一个高匿的ip代理池,速度是很快的。完整代码这里就不粘贴出来的,有兴趣的小伙伴点赞留言我可以私发呀。