mongodb

mongodb

使用代理处理反爬抓取陌陌文章

采集交流优采云 发表了文章 • 0 个评论 • 366 次浏览 • 2020-06-06 08:01 • 来自相关话题

  使用代理反爬抓取陌陌文章,获取文章标题、内容、公众号等信息,并储存到MongoDB数据库中。
  如果要抓取微信公众号文章可以使用搜狗的搜索引擎,它会显示最新的文章,但是有两个问题须要你们注意:
  关于代理池的实现以及使用可以参考这篇文章:使用Redis+Flask维护动态代理池
  下图展示了具体的流程框架:
  /img/remote/36680
  def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')
  在流程框架部份我们谈到,在此即将使用搜狗搜索陌陌站点文章,首先使我们步入搜狗搜索界面,比如输入关键字景色,就会出现陌陌文章的列表。
  /img/remote/36681?w=1042&h=552
  从网页的url可以看出这是一个get恳求,只保留主要参数,可以把url简化为
  /img/remote/36682
  其中,“query”代表搜索的关键词,“type”代表搜索结果的类型,“type=1”表示搜索结果是微信公众号,“type=2”表示搜索结果是陌陌文章,“page”也就是当前页数。
  分析完网页的url组成以后,我们先解决第一个问题:保存cookie,模拟登陆。
  打开浏览器控制台,选择NetWork->Headers选项卡,可以看见恳求的headers信息。
  /img/remote/36683
  解决完以上问题以后,让我们尝试写一下代码获取第1-100页的网页源码:
  from urllib.parse import urlencode
import requests
base_url = ''
# 构造请求头
headers = {
'Cookie': 'CXID=DF1F2AE56903B8B6289106D60E0C1339; SUID=F5959E3D8483920A000000005BCEB8CD; sw_uuid=3544458569; ssuid=8026096631; pex=C864C03270DED3DD8A06887A372DA219231FFAC25A9D64AE09E82AED12E416AC; SUV=00140F4F78C27EE25BF168CF5C981926; ad=p7R@vkllll2bio@ZlllllVsE@EclllllNBENLlllll9lllllpA7ll5@@@@@@@@@@; IPLOC=CN4110; ABTEST=2|1543456547|v1; weixinIndexVisited=1; sct=1; JSESSIONID=aaaXtNmDWRk5X5sEsy6Cw; PHPSESSID=lfgarg05due13kkgknnlbh3bq7; SUIR=EF72CF750D0876CFF631992E0D94BE34;',
'Host': '',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
}
def get_html(url, count=1):
response = requests.get(url, allow_redirects=False, headers=headers)
# 判断网页返回的状态码是否正常
# 如果状态码是200说明可以正常访问
if response.status_code == 200:
return response.text
# 如果状态码是302,则说明IP已经被封
if response.status_code == 302:
return None
def get_index(keyword, page):
data = {
'query': keyword,
'type': 2,
'page': page
}
queries = urlencode(data)
url = base_url + queries
html = get_html(url)
return html
def main():
for page in range(1, 101):
html = get_index('风景', page)
print(html)

if __name__ == '__main__':
main()
  运行以上代码,会发觉刚开始运行正常,正确返回网页源码,之后便始终返回None,此时使我们打开浏览器观察一下:
  /img/remote/36684
  可以看出,代码运行后不停返回None的缘由是网页被重定向,需要输入验证码能够正常访问,这便是我们开篇说过的第二个问题,我们的访问被搜狗搜索的反爬虫举措拦截,如果想要继续正常访问,便须要借助代理池获取随机代理来绕开反爬机制。
  在使用Redis+Flask维护动态代理池一文中,我们讲解了代理池的基本原理和简单实现,代码已托管到github上,现在使我们借助代理池来获取随机代理。
  首先使我们定义get_proxy()方法,返回代理池获取的随机可用ip:
  # flask监听的是5000端口
PROXY_POOL_URL = ':5000/get'
def get_proxy():
try:
response = requests.get(PROXY_POOL_URL)
if response.status_code == 200:
return response.text
return None
except ConnectionError:
return None
  接下来更改get_html(url, count=1)方法,以随机ip的形式访问网页:
  MAX_COUNT = 5
proxy = None
def get_html(url, count=1):
# 打印抓取的url
print('Crawling', url)
# 打印尝试抓取的次数
print('Trying Count', count)
global proxy
# 如果抓取的次数大于最大次数,则返回None
if count >= MAX_COUNT:
print('Tried Too Many Counts')
return None
try:
if proxy:
proxies = {
'http': 'http://' + proxy
}
# allow_redirects=False:禁止浏览器自动处理302跳转
response = requests.get(url, allow_redirects=False, headers=headers, proxies=proxies)
else:
response = requests.get(url, allow_redirects=False, headers=headers)
if response.status_code == 200:
return response.text
# 状态码是302,说明IP已经被封,调用get_proxy()获取新的ip
if response.status_code == 302:
# Need Proxy
print('302')
proxy = get_proxy()
if proxy:
print('Using Proxy', proxy)
return get_html(url)
else:
print('Get Proxy Failed')
return None
except ConnectionError as e:
# 如果连接超时,重新调用get_html(url, count)方法
print('Error Occurred', e.args)
proxy = get_proxy()
count += 1
return get_html(url, count)
  再次运行代码,会发觉不停重复复印None的情况基本消失。大家注意,这里是基本消失,原因是我们的代理池使用的是免费代理网站获取的代理搜狗微信 反爬虫,同一时刻可能会有许多人访问,这样就很容易导致ip地址被封的情况。如果你想要获取更好的疗效,不妨使用一下收费代理。
  至此,我们解决了开篇提及的两个问题,接下来,就可以抓取网页,分析内容。
  首先我们须要获取到第1-100页中每一篇文章的url:
  /img/remote/36685
  def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')

def main():
for page in range(1, 101):
html = get_index(KEYWORD, page)
if html:
article_urls = parse_index(html)
print(article_urls)
  获取到每一篇文章的url以后,就须要解析每一篇文章的内容。解析方式与前面相同,在此不再赘言。具体代码如下:
  def parse_detail(html):
try:
doc = pq(html)
title = doc('.rich_media_title').text()
content = doc('.rich_media_content ').text()
date = doc('#publish_time').text()
nickname = doc('.rich_media_meta_list .rich_media_meta_nickname').text()
wechat = doc('#activity-name').text()
return {
'title': title,
'content': content,
'date': date,
'nickname': nickname,
'wechat': wechat
}
except XMLSyntaxError:
return None
  需要注意的一点就是须要捕获XMLSyntaxError异常。
  最后使我们新建一个config.py文件,文件中包含了MongoDB的URL搜狗微信 反爬虫,数据库名称,表名称等常量:
  MONGO_URL = 'localhost'
MONGO_DB = 'weixin'
  在spider.py中配置储存到MongoDB相关方式:
  from config import *
import pymongo
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]
def save_to_mongo(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'])
  运行代码,接下来在MongoDB中进行查看:
  /img/remote/36686
  项目完整代码已托管到github: 查看全部

  使用代理反爬抓取陌陌文章,获取文章标题、内容、公众号等信息,并储存到MongoDB数据库中。
  如果要抓取微信公众号文章可以使用搜狗的搜索引擎,它会显示最新的文章,但是有两个问题须要你们注意:
  关于代理池的实现以及使用可以参考这篇文章:使用Redis+Flask维护动态代理池
  下图展示了具体的流程框架:
  /img/remote/36680
  def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')
  在流程框架部份我们谈到,在此即将使用搜狗搜索陌陌站点文章,首先使我们步入搜狗搜索界面,比如输入关键字景色,就会出现陌陌文章的列表。
  /img/remote/36681?w=1042&h=552
  从网页的url可以看出这是一个get恳求,只保留主要参数,可以把url简化为
  /img/remote/36682
  其中,“query”代表搜索的关键词,“type”代表搜索结果的类型,“type=1”表示搜索结果是微信公众号,“type=2”表示搜索结果是陌陌文章,“page”也就是当前页数。
  分析完网页的url组成以后,我们先解决第一个问题:保存cookie,模拟登陆。
  打开浏览器控制台,选择NetWork->Headers选项卡,可以看见恳求的headers信息。
  /img/remote/36683
  解决完以上问题以后,让我们尝试写一下代码获取第1-100页的网页源码:
  from urllib.parse import urlencode
import requests
base_url = ''
# 构造请求头
headers = {
'Cookie': 'CXID=DF1F2AE56903B8B6289106D60E0C1339; SUID=F5959E3D8483920A000000005BCEB8CD; sw_uuid=3544458569; ssuid=8026096631; pex=C864C03270DED3DD8A06887A372DA219231FFAC25A9D64AE09E82AED12E416AC; SUV=00140F4F78C27EE25BF168CF5C981926; ad=p7R@vkllll2bio@ZlllllVsE@EclllllNBENLlllll9lllllpA7ll5@@@@@@@@@@; IPLOC=CN4110; ABTEST=2|1543456547|v1; weixinIndexVisited=1; sct=1; JSESSIONID=aaaXtNmDWRk5X5sEsy6Cw; PHPSESSID=lfgarg05due13kkgknnlbh3bq7; SUIR=EF72CF750D0876CFF631992E0D94BE34;',
'Host': '',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
}
def get_html(url, count=1):
response = requests.get(url, allow_redirects=False, headers=headers)
# 判断网页返回的状态码是否正常
# 如果状态码是200说明可以正常访问
if response.status_code == 200:
return response.text
# 如果状态码是302,则说明IP已经被封
if response.status_code == 302:
return None
def get_index(keyword, page):
data = {
'query': keyword,
'type': 2,
'page': page
}
queries = urlencode(data)
url = base_url + queries
html = get_html(url)
return html
def main():
for page in range(1, 101):
html = get_index('风景', page)
print(html)

if __name__ == '__main__':
main()
  运行以上代码,会发觉刚开始运行正常,正确返回网页源码,之后便始终返回None,此时使我们打开浏览器观察一下:
  /img/remote/36684
  可以看出,代码运行后不停返回None的缘由是网页被重定向,需要输入验证码能够正常访问,这便是我们开篇说过的第二个问题,我们的访问被搜狗搜索的反爬虫举措拦截,如果想要继续正常访问,便须要借助代理池获取随机代理来绕开反爬机制。
  在使用Redis+Flask维护动态代理池一文中,我们讲解了代理池的基本原理和简单实现,代码已托管到github上,现在使我们借助代理池来获取随机代理。
  首先使我们定义get_proxy()方法,返回代理池获取的随机可用ip:
  # flask监听的是5000端口
PROXY_POOL_URL = ':5000/get'
def get_proxy():
try:
response = requests.get(PROXY_POOL_URL)
if response.status_code == 200:
return response.text
return None
except ConnectionError:
return None
  接下来更改get_html(url, count=1)方法,以随机ip的形式访问网页:
  MAX_COUNT = 5
proxy = None
def get_html(url, count=1):
# 打印抓取的url
print('Crawling', url)
# 打印尝试抓取的次数
print('Trying Count', count)
global proxy
# 如果抓取的次数大于最大次数,则返回None
if count >= MAX_COUNT:
print('Tried Too Many Counts')
return None
try:
if proxy:
proxies = {
'http': 'http://' + proxy
}
# allow_redirects=False:禁止浏览器自动处理302跳转
response = requests.get(url, allow_redirects=False, headers=headers, proxies=proxies)
else:
response = requests.get(url, allow_redirects=False, headers=headers)
if response.status_code == 200:
return response.text
# 状态码是302,说明IP已经被封,调用get_proxy()获取新的ip
if response.status_code == 302:
# Need Proxy
print('302')
proxy = get_proxy()
if proxy:
print('Using Proxy', proxy)
return get_html(url)
else:
print('Get Proxy Failed')
return None
except ConnectionError as e:
# 如果连接超时,重新调用get_html(url, count)方法
print('Error Occurred', e.args)
proxy = get_proxy()
count += 1
return get_html(url, count)
  再次运行代码,会发觉不停重复复印None的情况基本消失。大家注意,这里是基本消失,原因是我们的代理池使用的是免费代理网站获取的代理搜狗微信 反爬虫,同一时刻可能会有许多人访问,这样就很容易导致ip地址被封的情况。如果你想要获取更好的疗效,不妨使用一下收费代理。
  至此,我们解决了开篇提及的两个问题,接下来,就可以抓取网页,分析内容。
  首先我们须要获取到第1-100页中每一篇文章的url:
  /img/remote/36685
  def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')

def main():
for page in range(1, 101):
html = get_index(KEYWORD, page)
if html:
article_urls = parse_index(html)
print(article_urls)
  获取到每一篇文章的url以后,就须要解析每一篇文章的内容。解析方式与前面相同,在此不再赘言。具体代码如下:
  def parse_detail(html):
try:
doc = pq(html)
title = doc('.rich_media_title').text()
content = doc('.rich_media_content ').text()
date = doc('#publish_time').text()
nickname = doc('.rich_media_meta_list .rich_media_meta_nickname').text()
wechat = doc('#activity-name').text()
return {
'title': title,
'content': content,
'date': date,
'nickname': nickname,
'wechat': wechat
}
except XMLSyntaxError:
return None
  需要注意的一点就是须要捕获XMLSyntaxError异常。
  最后使我们新建一个config.py文件,文件中包含了MongoDB的URL搜狗微信 反爬虫,数据库名称,表名称等常量:
  MONGO_URL = 'localhost'
MONGO_DB = 'weixin'
  在spider.py中配置储存到MongoDB相关方式:
  from config import *
import pymongo
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]
def save_to_mongo(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'])
  运行代码,接下来在MongoDB中进行查看:
  /img/remote/36686
  项目完整代码已托管到github:

使用代理处理反爬抓取陌陌文章

采集交流优采云 发表了文章 • 0 个评论 • 366 次浏览 • 2020-06-06 08:01 • 来自相关话题

  使用代理反爬抓取陌陌文章,获取文章标题、内容、公众号等信息,并储存到MongoDB数据库中。
  如果要抓取微信公众号文章可以使用搜狗的搜索引擎,它会显示最新的文章,但是有两个问题须要你们注意:
  关于代理池的实现以及使用可以参考这篇文章:使用Redis+Flask维护动态代理池
  下图展示了具体的流程框架:
  /img/remote/36680
  def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')
  在流程框架部份我们谈到,在此即将使用搜狗搜索陌陌站点文章,首先使我们步入搜狗搜索界面,比如输入关键字景色,就会出现陌陌文章的列表。
  /img/remote/36681?w=1042&h=552
  从网页的url可以看出这是一个get恳求,只保留主要参数,可以把url简化为
  /img/remote/36682
  其中,“query”代表搜索的关键词,“type”代表搜索结果的类型,“type=1”表示搜索结果是微信公众号,“type=2”表示搜索结果是陌陌文章,“page”也就是当前页数。
  分析完网页的url组成以后,我们先解决第一个问题:保存cookie,模拟登陆。
  打开浏览器控制台,选择NetWork->Headers选项卡,可以看见恳求的headers信息。
  /img/remote/36683
  解决完以上问题以后,让我们尝试写一下代码获取第1-100页的网页源码:
  from urllib.parse import urlencode
import requests
base_url = ''
# 构造请求头
headers = {
'Cookie': 'CXID=DF1F2AE56903B8B6289106D60E0C1339; SUID=F5959E3D8483920A000000005BCEB8CD; sw_uuid=3544458569; ssuid=8026096631; pex=C864C03270DED3DD8A06887A372DA219231FFAC25A9D64AE09E82AED12E416AC; SUV=00140F4F78C27EE25BF168CF5C981926; ad=p7R@vkllll2bio@ZlllllVsE@EclllllNBENLlllll9lllllpA7ll5@@@@@@@@@@; IPLOC=CN4110; ABTEST=2|1543456547|v1; weixinIndexVisited=1; sct=1; JSESSIONID=aaaXtNmDWRk5X5sEsy6Cw; PHPSESSID=lfgarg05due13kkgknnlbh3bq7; SUIR=EF72CF750D0876CFF631992E0D94BE34;',
'Host': '',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
}
def get_html(url, count=1):
response = requests.get(url, allow_redirects=False, headers=headers)
# 判断网页返回的状态码是否正常
# 如果状态码是200说明可以正常访问
if response.status_code == 200:
return response.text
# 如果状态码是302,则说明IP已经被封
if response.status_code == 302:
return None
def get_index(keyword, page):
data = {
'query': keyword,
'type': 2,
'page': page
}
queries = urlencode(data)
url = base_url + queries
html = get_html(url)
return html
def main():
for page in range(1, 101):
html = get_index('风景', page)
print(html)

if __name__ == '__main__':
main()
  运行以上代码,会发觉刚开始运行正常,正确返回网页源码,之后便始终返回None,此时使我们打开浏览器观察一下:
  /img/remote/36684
  可以看出,代码运行后不停返回None的缘由是网页被重定向,需要输入验证码能够正常访问,这便是我们开篇说过的第二个问题,我们的访问被搜狗搜索的反爬虫举措拦截,如果想要继续正常访问,便须要借助代理池获取随机代理来绕开反爬机制。
  在使用Redis+Flask维护动态代理池一文中,我们讲解了代理池的基本原理和简单实现,代码已托管到github上,现在使我们借助代理池来获取随机代理。
  首先使我们定义get_proxy()方法,返回代理池获取的随机可用ip:
  # flask监听的是5000端口
PROXY_POOL_URL = ':5000/get'
def get_proxy():
try:
response = requests.get(PROXY_POOL_URL)
if response.status_code == 200:
return response.text
return None
except ConnectionError:
return None
  接下来更改get_html(url, count=1)方法,以随机ip的形式访问网页:
  MAX_COUNT = 5
proxy = None
def get_html(url, count=1):
# 打印抓取的url
print('Crawling', url)
# 打印尝试抓取的次数
print('Trying Count', count)
global proxy
# 如果抓取的次数大于最大次数,则返回None
if count >= MAX_COUNT:
print('Tried Too Many Counts')
return None
try:
if proxy:
proxies = {
'http': 'http://' + proxy
}
# allow_redirects=False:禁止浏览器自动处理302跳转
response = requests.get(url, allow_redirects=False, headers=headers, proxies=proxies)
else:
response = requests.get(url, allow_redirects=False, headers=headers)
if response.status_code == 200:
return response.text
# 状态码是302,说明IP已经被封,调用get_proxy()获取新的ip
if response.status_code == 302:
# Need Proxy
print('302')
proxy = get_proxy()
if proxy:
print('Using Proxy', proxy)
return get_html(url)
else:
print('Get Proxy Failed')
return None
except ConnectionError as e:
# 如果连接超时,重新调用get_html(url, count)方法
print('Error Occurred', e.args)
proxy = get_proxy()
count += 1
return get_html(url, count)
  再次运行代码,会发觉不停重复复印None的情况基本消失。大家注意,这里是基本消失,原因是我们的代理池使用的是免费代理网站获取的代理搜狗微信 反爬虫,同一时刻可能会有许多人访问,这样就很容易导致ip地址被封的情况。如果你想要获取更好的疗效,不妨使用一下收费代理。
  至此,我们解决了开篇提及的两个问题,接下来,就可以抓取网页,分析内容。
  首先我们须要获取到第1-100页中每一篇文章的url:
  /img/remote/36685
  def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')

def main():
for page in range(1, 101):
html = get_index(KEYWORD, page)
if html:
article_urls = parse_index(html)
print(article_urls)
  获取到每一篇文章的url以后,就须要解析每一篇文章的内容。解析方式与前面相同,在此不再赘言。具体代码如下:
  def parse_detail(html):
try:
doc = pq(html)
title = doc('.rich_media_title').text()
content = doc('.rich_media_content ').text()
date = doc('#publish_time').text()
nickname = doc('.rich_media_meta_list .rich_media_meta_nickname').text()
wechat = doc('#activity-name').text()
return {
'title': title,
'content': content,
'date': date,
'nickname': nickname,
'wechat': wechat
}
except XMLSyntaxError:
return None
  需要注意的一点就是须要捕获XMLSyntaxError异常。
  最后使我们新建一个config.py文件,文件中包含了MongoDB的URL搜狗微信 反爬虫,数据库名称,表名称等常量:
  MONGO_URL = 'localhost'
MONGO_DB = 'weixin'
  在spider.py中配置储存到MongoDB相关方式:
  from config import *
import pymongo
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]
def save_to_mongo(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'])
  运行代码,接下来在MongoDB中进行查看:
  /img/remote/36686
  项目完整代码已托管到github: 查看全部

  使用代理反爬抓取陌陌文章,获取文章标题、内容、公众号等信息,并储存到MongoDB数据库中。
  如果要抓取微信公众号文章可以使用搜狗的搜索引擎,它会显示最新的文章,但是有两个问题须要你们注意:
  关于代理池的实现以及使用可以参考这篇文章:使用Redis+Flask维护动态代理池
  下图展示了具体的流程框架:
  /img/remote/36680
  def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')
  在流程框架部份我们谈到,在此即将使用搜狗搜索陌陌站点文章,首先使我们步入搜狗搜索界面,比如输入关键字景色,就会出现陌陌文章的列表。
  /img/remote/36681?w=1042&h=552
  从网页的url可以看出这是一个get恳求,只保留主要参数,可以把url简化为
  /img/remote/36682
  其中,“query”代表搜索的关键词,“type”代表搜索结果的类型,“type=1”表示搜索结果是微信公众号,“type=2”表示搜索结果是陌陌文章,“page”也就是当前页数。
  分析完网页的url组成以后,我们先解决第一个问题:保存cookie,模拟登陆。
  打开浏览器控制台,选择NetWork->Headers选项卡,可以看见恳求的headers信息。
  /img/remote/36683
  解决完以上问题以后,让我们尝试写一下代码获取第1-100页的网页源码:
  from urllib.parse import urlencode
import requests
base_url = ''
# 构造请求头
headers = {
'Cookie': 'CXID=DF1F2AE56903B8B6289106D60E0C1339; SUID=F5959E3D8483920A000000005BCEB8CD; sw_uuid=3544458569; ssuid=8026096631; pex=C864C03270DED3DD8A06887A372DA219231FFAC25A9D64AE09E82AED12E416AC; SUV=00140F4F78C27EE25BF168CF5C981926; ad=p7R@vkllll2bio@ZlllllVsE@EclllllNBENLlllll9lllllpA7ll5@@@@@@@@@@; IPLOC=CN4110; ABTEST=2|1543456547|v1; weixinIndexVisited=1; sct=1; JSESSIONID=aaaXtNmDWRk5X5sEsy6Cw; PHPSESSID=lfgarg05due13kkgknnlbh3bq7; SUIR=EF72CF750D0876CFF631992E0D94BE34;',
'Host': '',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
}
def get_html(url, count=1):
response = requests.get(url, allow_redirects=False, headers=headers)
# 判断网页返回的状态码是否正常
# 如果状态码是200说明可以正常访问
if response.status_code == 200:
return response.text
# 如果状态码是302,则说明IP已经被封
if response.status_code == 302:
return None
def get_index(keyword, page):
data = {
'query': keyword,
'type': 2,
'page': page
}
queries = urlencode(data)
url = base_url + queries
html = get_html(url)
return html
def main():
for page in range(1, 101):
html = get_index('风景', page)
print(html)

if __name__ == '__main__':
main()
  运行以上代码,会发觉刚开始运行正常,正确返回网页源码,之后便始终返回None,此时使我们打开浏览器观察一下:
  /img/remote/36684
  可以看出,代码运行后不停返回None的缘由是网页被重定向,需要输入验证码能够正常访问,这便是我们开篇说过的第二个问题,我们的访问被搜狗搜索的反爬虫举措拦截,如果想要继续正常访问,便须要借助代理池获取随机代理来绕开反爬机制。
  在使用Redis+Flask维护动态代理池一文中,我们讲解了代理池的基本原理和简单实现,代码已托管到github上,现在使我们借助代理池来获取随机代理。
  首先使我们定义get_proxy()方法,返回代理池获取的随机可用ip:
  # flask监听的是5000端口
PROXY_POOL_URL = ':5000/get'
def get_proxy():
try:
response = requests.get(PROXY_POOL_URL)
if response.status_code == 200:
return response.text
return None
except ConnectionError:
return None
  接下来更改get_html(url, count=1)方法,以随机ip的形式访问网页:
  MAX_COUNT = 5
proxy = None
def get_html(url, count=1):
# 打印抓取的url
print('Crawling', url)
# 打印尝试抓取的次数
print('Trying Count', count)
global proxy
# 如果抓取的次数大于最大次数,则返回None
if count >= MAX_COUNT:
print('Tried Too Many Counts')
return None
try:
if proxy:
proxies = {
'http': 'http://' + proxy
}
# allow_redirects=False:禁止浏览器自动处理302跳转
response = requests.get(url, allow_redirects=False, headers=headers, proxies=proxies)
else:
response = requests.get(url, allow_redirects=False, headers=headers)
if response.status_code == 200:
return response.text
# 状态码是302,说明IP已经被封,调用get_proxy()获取新的ip
if response.status_code == 302:
# Need Proxy
print('302')
proxy = get_proxy()
if proxy:
print('Using Proxy', proxy)
return get_html(url)
else:
print('Get Proxy Failed')
return None
except ConnectionError as e:
# 如果连接超时,重新调用get_html(url, count)方法
print('Error Occurred', e.args)
proxy = get_proxy()
count += 1
return get_html(url, count)
  再次运行代码,会发觉不停重复复印None的情况基本消失。大家注意,这里是基本消失,原因是我们的代理池使用的是免费代理网站获取的代理搜狗微信 反爬虫,同一时刻可能会有许多人访问,这样就很容易导致ip地址被封的情况。如果你想要获取更好的疗效,不妨使用一下收费代理。
  至此,我们解决了开篇提及的两个问题,接下来,就可以抓取网页,分析内容。
  首先我们须要获取到第1-100页中每一篇文章的url:
  /img/remote/36685
  def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')

def main():
for page in range(1, 101):
html = get_index(KEYWORD, page)
if html:
article_urls = parse_index(html)
print(article_urls)
  获取到每一篇文章的url以后,就须要解析每一篇文章的内容。解析方式与前面相同,在此不再赘言。具体代码如下:
  def parse_detail(html):
try:
doc = pq(html)
title = doc('.rich_media_title').text()
content = doc('.rich_media_content ').text()
date = doc('#publish_time').text()
nickname = doc('.rich_media_meta_list .rich_media_meta_nickname').text()
wechat = doc('#activity-name').text()
return {
'title': title,
'content': content,
'date': date,
'nickname': nickname,
'wechat': wechat
}
except XMLSyntaxError:
return None
  需要注意的一点就是须要捕获XMLSyntaxError异常。
  最后使我们新建一个config.py文件,文件中包含了MongoDB的URL搜狗微信 反爬虫,数据库名称,表名称等常量:
  MONGO_URL = 'localhost'
MONGO_DB = 'weixin'
  在spider.py中配置储存到MongoDB相关方式:
  from config import *
import pymongo
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]
def save_to_mongo(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'])
  运行代码,接下来在MongoDB中进行查看:
  /img/remote/36686
  项目完整代码已托管到github:

官方客服QQ群

微信人工客服

QQ人工客服


线