资讯专栏INFORMATION COLUMN

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

QiShare / 471人阅读

摘要:目标使用代理反爬抓取微信文章,获取文章标题内容公众号等信息,并存储到数据库中。代理设置在使用维护动态代理池一文中,我们讲解了代理池的基本原理和简单实现,代码已托管到上,现在让我们利用代理池来获取随机代理。

目标

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

流程框架

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

如果要抓取某一个主题(比如微信风景文章)的所有记录的话,需要先登录(也就是你的请求头headers中要有登陆之后服务器返回的cookies),未登录只可以查看10页,登录之后可以查看100页

搜狗微信站点的反爬措施比较严格,如果只是用本地IP(单IP)去抓取的话肯定是不行的,这个时候我们需要用到代理池技术(通过可用随机代理去绕过反爬机制)

关于代理池的实现以及使用可以参考这篇文章:使用Redis+Flask维护动态代理池

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

(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/,比如输入关键字风景,就会出现微信文章的列表。

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

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

分析完网页的url组成之后,我们先解决第一个问题:保存cookie,模拟登录。
打开浏览器控制台,选择NetWork->Headers选项卡,可以看到请求的headers信息。

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

from urllib.parse import urlencode
import requests

base_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 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,此时让我们打开浏览器观察一下:

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

(2)代理设置

在使用Redis+Flask维护动态代理池一文中,我们讲解了代理池的基本原理和简单实现,代码已托管到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 = 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地址被封的情况。如果你想要获取更好的效果,不妨使用一下收费代理。

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

(3)分析详情页内容

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

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 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中进行查看:

项目完整代码已托管到github:https://github.com/panjings/p...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42845.html

相关文章

  • 首次公开,整理12年积累的博客收藏夹,零距离展示《收藏夹吃灰》系列博客

    摘要:时间永远都过得那么快,一晃从年注册,到现在已经过去了年那些被我藏在收藏夹吃灰的文章,已经太多了,是时候把他们整理一下了。那是因为收藏夹太乱,橡皮擦给设置私密了,不收拾不好看呀。 ...

    Harriet666 评论0 收藏0
  • 听说你好不容易写了个爬虫,结果没抓几个就被封了?(附工具)

    摘要:近来知乎上如雨后春笋般冒出了大把大把的爬虫教程。一个爬虫教程的案例三个月之后还能有效已经是万幸了。不过仍然要说明,即使经过多次验证,也无法保证一个的绝对可用性。这是个概率问题,工具的作用只是尽可能提高概率。 近来知乎上如雨后春笋般冒出了大把大把的爬虫教程。这是好事,学了 Python 基础的同学们可以很轻松地搜出许多练手的例子。不过我不是针对谁,我是说网上绝大多数的爬虫教程,其实都缺乏...

    Pluser 评论0 收藏0
  • Python爬虫学习路线

    摘要:以下这些项目,你拿来学习学习练练手。当你每个步骤都能做到很优秀的时候,你应该考虑如何组合这四个步骤,使你的爬虫达到效率最高,也就是所谓的爬虫策略问题,爬虫策略学习不是一朝一夕的事情,建议多看看一些比较优秀的爬虫的设计方案,比如说。 (一)如何学习Python 学习Python大致可以分为以下几个阶段: 1.刚上手的时候肯定是先过一遍Python最基本的知识,比如说:变量、数据结构、语法...

    liaoyg8023 评论0 收藏0
  • 极简爬虫攻防战纪要

    摘要:极简爬虫攻防战纪要爬虫是构建搜索引擎的基础负责抓取网页信息并对网页识别分类及过滤。爬虫方终于锁定了第一场战役的胜局由于断崖式技术的出现,反爬方在浏览器识别战役上望风披靡。经过反爬方的精心运作,逐渐有效削弱了敌方的攻势。 极简爬虫攻防战纪要     爬虫是构建搜索引擎的基础, 负责抓取网页信息并对网页识别、分类及过滤。我们熟识的电商、搜索、新闻及各大门户网站都有强大的爬虫集群在每...

    elliott_hu 评论0 收藏0
  • 爬虫+网站开发实例:电影票比价网

    摘要:注一篇去年的旧文,发现没在知乎发过,过来补个档。于是就有了我们这个小项目电影票比价网在我们这个网页上,会展示出当前热映的电影。涉及到模块主要是用来匹配不同渠道的影院信息代码结构项目主要有三块使用豆瓣每日更新上映的影片列表。 注:一篇去年的旧文,发现没在知乎发过,过来补个档。有个小问题是项目中淘票票的网页反爬提升且变动较多,目前暂不可用了。 时常有同学会问我类似的问题:我已经学完了 Py...

    Codeing_ls 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<