声明:此篇文章主要是观看静觅教学视频后做的笔记,原教程地址:https://cuiqingcai.com/
在抓取网页时,某些网站会有封ip的现象,所以选择利用代理伪装我们的ip进行爬虫请求,但进行爬虫时可能需要很多ip,这时就要求维护一个代理池(池也就是代理队列),可放进代理,也可取出代理。我就选择的崔庆才老师维护的代理池,是用Flask和Redis维护的一个代理池。
代理池设计思路
1.从各大网站获取免费的有用代理
2.用Redis来维护池的队列存储
3.维护池,剔除无用的代理,获得有用的代理
4.Flask是实现代理池的一个接口,返回到web上
具体实现请看:Flask+Redis维护代理池
抓取微信文章思路
1.通过搜狗爬取微信文章
2.前十页信息不需要验证,10页到100页需要微信登录验证
3.发现浏览多页后会返回302状态码,跳到反爬虫的页面,此时的ip已经被封,需要输入3次验证码才能继续浏览
流程框架
1.抓取索引页内容:利用requests请求目标站点,得到索引网页HTML代码,返回结果。
2.代理设置:如果遇到302状态码,则证明IP被封,切换代理重试
3.分析详情页内容:请求详情页,分析得到标题,正文等内容
4.将数据保存到数据库
实现步骤
1.打开搜狗搜索引擎,进入:http://weixin.sogou.com/weixin?
2.实现从代理池中获取代理
import requests
from requests.exceptions import ConnectionError
PROXY_POOL_URL = 'http://127.0.0.1:5000/get'
def get_proxy():
"""
作用:从代理池中获取一个代理IP
"""
try:
# 运行代理池,从Flask网页中读取IP
response = requests.get(PROXY_POOL_URL)
if response.status_code == 200:
return response.text
# 如果状态码不是200,也返回None
return None
except ConnectionError:
return None
3.定义一个get_html函数,利用代理来获取页面源码,并通过url形参来传入访问地址
from requests.exceptions import ConnectionError
import requests
proxy = None
MAX_COUNT = 5
headers = {
'Cookie': 'SUID=F6177C7B3220910A000000058E4D679; SUV=1491392122762346; ABTEST=1|1491392129|v1; SNUID=0DED8681FBFEB69230E6BF3DFB2F8D6B; ld=OZllllllll2Yi2balllllV06C77lllllWTZgdkllll9lllllxv7ll5@@@@@@@@@@; LSTMV=189%2C31; LCLKINT=1805; weixinIndexVisited=1; SUIR=0DED8681FBFEB69230E6BF3DFB2F8D6B; JSESSIONID=aaa-BcHIDk9xYdr4odFSv; PHPSESSID=afohijek3ju93ab6l0eqeph902; sct=21; IPLOC=CN; ppinf=5|1491580643|1492790243|dHJ1c3Q6MToxfGNsaWVudGlkOjQ6MjAxN3x1bmlxbmFtZToyNzolRTUlQjQlOTQlRTUlQkElODYlRTYlODklOER8Y3J0OjEwOjE0OTE1ODA2NDN8cmVmbmljazoyNzolRTUlQjQlOTQlRTUlQkElODYlRTYlODklOER8dXNlcmlkOjQ0Om85dDJsdUJfZWVYOGRqSjRKN0xhNlBta0RJODRAd2VpeGluLnNvaHUuY29tfA; pprdig=j7ojfJRegMrYrl96LmzUhNq-RujAWyuXT_H3xZba8nNtaj7NKA5d0ORq-yoqedkBg4USxLzmbUMnIVsCUjFciRnHDPJ6TyNrurEdWT_LvHsQIKkygfLJH-U2MJvhwtHuW09enCEzcDAA_GdjwX6_-_fqTJuv9w9Gsw4rF9xfGf4; sgid=; ppmdig=1491580643000000d6ae8b0ebe76bbd1844c993d1ff47cea',
'Host': 'weixin.sogou.com',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
}
def get_html(url, count=1):
"""
作用:通过代理访问与正常访问来回切换,获得页面源码
url: 需要返回的页面url地址
count: 捕获异常次数,即错误尝试,默认为1
"""
print('Crawling', url)
print('Trying Count', count)
# 定义一个proxy的全局变量
global proxy
# 判断count次数是否超过最大次数,就返回None
if count >= MAX_COUNT:
print('Tried Too Many Counts')
return None
try:
# 判断是否从代理池中拿到proxy代理
if proxy:
proxies = {
'http': 'http://' + proxy
}
# 使用代理访问,allow_redirects=False表示关闭默认的重定向跳转
response = requests.get(url, allow_redirects=False, headers=headers, proxies=proxies)
else:
# 没有拿到代理则使用正常IP访问
response = requests.get(url, allow_redirects=False, headers=headers)
if response.status_code == 200:
return response.text
if response.status_code == 302:
# Need Proxy
print('302')
# 页面出现302状态码后,重新获取一个代理IP,并使用这个IP
proxy = get_proxy()
if proxy:
print('Using Proxy', proxy)
# 递归调用当前函数获取页面源码
return get_html(url)
else:
print('Get Proxy Failed')
return None
except ConnectionError as e:
print('Error Occurred', e.args)
proxy = get_proxy()
# 捕获到异常,错误尝试次数+1,超过规定次数就退出当前if语句执行
count += 1
return get_html(url, count)
3.以搜索关键词Python为例,抓取Python相关微信文章,然后使用Network选项卡的Doc选出访问目标网址的参数,可以看到如下内容
其中Headers的内容是需要加到请求头的,Query String Parameters的内容是请求参数,可通过urlencode转码拼接成最终访问URL,那么就有
from urllib.parse import urlencode
base_url = 'http://weixin.sogou.com/weixin?'
KEYWORD = 'python'
def get_index(keyword, page):
"""
作用: 主要是调用get_html函数,实现与get_html函数功能相同
keyword: 搜索的关键字
page:访问的页码
"""
data = {
'query': keyword,
'type': 2,
'page': page
}
# 将get的参数进行转码
queries = urlencode(data)
url = base_url + queries
html = get_html(url)
return html
4.接下来,通过get_index函数拿到了源码之后,就需要对网页元素进行解析,找到每篇文章的链接,也就是a标签
可以发现,该a标签的选择器为.news-box .news-list li .txt-box h3 a,那么可以通过pyquery解析库来进行解析
from pyquery import PyQuery as pq
def parse_index(html):
"""
作用:使用pyquery库对网页进行解析,并提取文章的a标签跳转url
html: 网页源码,即通过get_index函数获得
"""
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')
5.再通过get_detail函数访问每个详情页链接,并通过parse_detail函数解析详情页,获取每篇文章的内容
from pyquery import PyQuery as pq
from lxml.etree import XMLSyntaxError
import requests
def get_detail(url):
"""
作用: 获取每篇文章的详情页网页源码
url: 详情页url地址
"""
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
except ConnectionError:
return None
def parse_detail(html):
"""
作用: 对每篇文章的详情页进行解析,使用pyqery库对网页进行解析
html: 详情页的网页源码,通过get_detail函数获得
"""
try:
doc = pq(html)
title = doc('.rich_media_title').text()
content = doc('.rich_media_content').text()
date = doc('#post-date').text()
nickname = doc('#js_profile_qrcode > div > strong').text()
wechat = doc('#js_profile_qrcode > div > p:nth-child(3) > span').text()
return {
'title': title,
'content': content,
'date': date,
'nickname': nickname,
'wechat': wechat
}
except XMLSyntaxError:
return None
6.将字典形式的数据内容保存至MongoDB中
MONGO_URL = 'localhost'
MONGO_DB = 'weixin'
# 定义一个MongoDB本地客户端
client = pymongo.MongoClient(MONGO_URL)
# 在本地客户端创建一个数据库
db = client[MONGO_DB]
def save_to_mongo(data):
"""
作用: 将详情页解析的数据存储在MongoDB中
data: 需要存储的数据
"""
# 判断存储数据是否发生更新
if db['articles'].update({'title': data['title']}, {'$set': data}, True):
print('Saved to Mongo', data['title'])
else:
print('Saved to Mongo Failed', data['title'])
7.最后通过定义的爬虫调度器函数使用前面定义的功能模块,完成对微信文章的抓取
def WexinArticles_Spider():
# 通过cookie访问100页
for page in range(1, 101):
# 加入keyword即page参数,获得文章列表源码
html = get_index(KEYWORD, page)
if html:
# 对文章列表进行解析,拿到每篇文章的url地址
article_urls = parse_index(html)
for article_url in article_urls:
# 使用get_detail函数访问每篇文章详情页地址
article_html = get_detail(article_url)
if article_html:
# 解析每篇文章详情页数据
article_data = parse_detail(article_html)
print(article_data)
if article_data:
# 保存至MongoDB数据库
save_to_mongo(article_data)
8.完整源码地址:https://github.com/XiaoFei-97/WeXinArticles_Spider
原文出处:https://www.jzfblog.com/detail/72,文章的更新编辑以此链接为准。欢迎关注源站文章!