博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用代理处理反爬抓取微信文章
阅读量:6288 次
发布时间:2019-06-22

本文共 5902 字,大约阅读时间需要 19 分钟。

目标

使用代理反爬抓取微信文章,获取文章标题、内容、公众号等信息,并存储到MongoDB数据库中。

流程框架

如果要抓取微信公众号文章可以使用搜狗的搜索引擎,它会显示最新的文章,但是有两个问题需要大家注意:

  • 如果要抓取某一个主题(比如微信风景文章)的所有记录的话,需要先登录(也就是你的请求头headers中要有登陆之后服务器返回的cookies),未登录只可以查看10页,登录之后可以查看100页
  • 搜狗微信站点的反爬措施比较严格,如果只是用本地IP(单IP)去抓取的话肯定是不行的,这个时候我们需要用到代理池技术(通过可用随机代理去绕过反爬机制)
    关于代理池的实现以及使用可以参考这篇文章:

下图展示了具体的流程框架:

%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%90%86%E5%A4%84%E7%90%86%E5%8F%8D%E7%88%AC%E6%8A%93%E5%8F%96%E5%BE%AE%E4%BF%A1%E6%96%87%E7%AB%A0-1.png

(1)抓取索引页内容

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')

在流程框架部分我们提到,在此将要使用搜狗搜索微信站点文章,首先让我们进入搜狗搜索界面https://weixin.sogou.com/,比如输入关键字风景,就会出现微信文章的列表。

%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%90%86%E5%A4%84%E7%90%86%E5%8F%8D%E7%88%AC%E6%8A%93%E5%8F%96%E5%BE%AE%E4%BF%A1%E6%96%87%E7%AB%A0-2.png

从网页的url可以看出这是一个get请求,只保留主要参数,可以把url简化为

%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%90%86%E5%A4%84%E7%90%86%E5%8F%8D%E7%88%AC%E6%8A%93%E5%8F%96%E5%BE%AE%E4%BF%A1%E6%96%87%E7%AB%A0_3.png

其中,“query”代表搜索的关键词,“type”代表搜索结果的类型,“type=1”表示搜索结果是微信公众号,“type=2”表示搜索结果是微信文章,“page”也就是当前页数。

分析完网页的url组成之后,我们先解决第一个问题:保存cookie,模拟登录。

打开浏览器控制台,选择NetWork->Headers选项卡,可以看到请求的headers信息。

%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%90%86%E5%A4%84%E7%90%86%E5%8F%8D%E7%88%AC%E6%8A%93%E5%8F%96%E5%BE%AE%E4%BF%A1%E6%96%87%E7%AB%A0-4.png

解决完以上问题之后,让我们尝试写一下代码获取第1-100页的网页源码:

from urllib.parse import urlencodeimport requestsbase_url = 'https://weixin.sogou.com/weixin?'# 构造请求头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': 'weixin.sogou.com',    '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 Nonedef get_index(keyword, page):    data = {        'query': keyword,        'type': 2,        'page': page    }    queries = urlencode(data)    url = base_url + queries    html = get_html(url)    return htmldef main():    for page in range(1, 101):        html = get_index('风景', page)        print(html)        if __name__ == '__main__':    main()

运行以上代码,会发现刚开始运行正常,正确返回网页源码,之后便一直返回None,此时让我们打开浏览器观察一下:

%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%90%86%E5%A4%84%E7%90%86%E5%8F%8D%E7%88%AC%E6%8A%93%E5%8F%96%E5%BE%AE%E4%BF%A1%E6%96%87%E7%AB%A0-5.png

可以看出,代码运行后不停返回None的原因是网页被重定向,需要输入验证码才能正常访问,这便是我们开篇说过的第二个问题,我们的访问被搜狗搜索的反爬虫措施拦截,如果想要继续正常访问,便需要利用代理池获取随机代理来绕过反爬机制。

(2)代理设置

在一文中,我们讲解了代理池的基本原理和简单实现,代码已托管到github上,现在让我们利用代理池来获取随机代理。

首先让我们定义get_proxy()方法,返回代理池获取的随机可用ip:

# flask监听的是5000端口PROXY_POOL_URL = 'http://127.0.0.1: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 = 5proxy = Nonedef 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地址被封的情况。如果你想要获取更好的效果,不妨使用一下收费代理。

至此,我们解决了开篇提到的两个问题,接下来,就可以抓取网页,分析内容。

(3)分析详情页内容

首先我们需要获取到第1-100页中每一篇文章的url:

%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%90%86%E5%A4%84%E7%90%86%E5%8F%8D%E7%88%AC%E6%8A%93%E5%8F%96%E5%BE%AE%E4%BF%A1%E6%96%87%E7%AB%A0-6.png

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异常。

(4)将数据保存到数据库

最后让我们新建一个config.py文件,文件中包含了MongoDB的URL,数据库名称,表名称等常量:

MONGO_URL = 'localhost'MONGO_DB = 'weixin'

在spider.py中配置存储到MongoDB相关方法:

from config import *import pymongoclient = 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中进行查看:

%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%90%86%E5%A4%84%E7%90%86%E5%8F%8D%E7%88%AC%E6%8A%93%E5%8F%96%E5%BE%AE%E4%BF%A1%E6%96%87%E7%AB%A0-7.png

项目完整代码已托管到github:

转载于:https://www.cnblogs.com/woyouyihujiu/p/10057896.html

你可能感兴趣的文章
PHP中常见的面试题2(附答案)
查看>>
26.Azure备份服务器(下)
查看>>
mybatis学习
查看>>
LCD的接口类型详解
查看>>
Spring Boot Unregistering JMX-exposed beans on shutdown
查看>>
poi 导入导出的api说明(大全)
查看>>
Mono for Android 优势与劣势
查看>>
将图片转成base64字符串并在JSP页面显示的Java代码
查看>>
js 面试题
查看>>
sqoop数据迁移(基于Hadoop和关系数据库服务器之间传送数据)
查看>>
腾讯云下安装 nodejs + 实现 Nginx 反向代理
查看>>
Javascript 中的 Array 操作
查看>>
java中包容易出现的错误及权限问题
查看>>
AngularJS之初级Route【一】(六)
查看>>
服务器硬件问题整理的一点总结
查看>>
SAP S/4HANA Cloud: Revolutionizing the Next Generation of Cloud ERP
查看>>
Mellanox公司计划利用系统芯片提升存储产品速度
查看>>
白帽子守护网络安全,高薪酬成大学生就业首选!
查看>>
ARM想将芯片装进人类大脑 降低能耗是一大挑战
查看>>
Oracle数据库的备份方法
查看>>