使用代理抓取反爬微信文章

2018年08月09日 爬虫小案例 浏览(538) 评论(0)

简介声明:此篇文章主要是观看静觅教学视频后做的笔记,原教程地址:https://cuiqingcai.com/在抓取网页时,某些网站会有封ip的现象,所以选择利用代理伪装我们的ip进行爬虫请求,但进行爬虫时可能需要很多ip,这时就要求维护一个代理池(池也就是代理队列),可放进代理,也可取出代理。我...

声明:此篇文章主要是观看静觅教学视频后做的笔记,原教程地址: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选出访问目标网址的参数,可以看到如下内容

2018-08-09 13-31-27屏幕截图.png

    其中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标签

2018-08-09 14-09-11屏幕截图.png

    可以发现,该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

郑重声明:

(原创博文,转载请注明来自 蒋振飞的博客!本文链接:点击我获取我的链接)

若对你有帮助,不妨扫一扫右侧的二维码打赏我一下吧 ^_^

评论区域

评论列表

智慧如你,速度抢下沙发吧 !

更多文章目录