抓取微信文章:使用代理来处理反爬虫措施

2023-11-20

参考:崔庆才老师教程

目标网站分析

我们将从搜狗-微信这个网址来爬取微信的文章。
https://weixin.sogou.com/
在这里插入图片描述
输入“程序员”并搜索:
在这里插入图片描述
可以看到上方的URL有许多的信息,我们只保留query、type、page这几个参数即可。修改page可以实现翻页。
ps:登录后才能查看第10页之后的内容
在这里插入图片描述
打开审查->Network->勾选Preserve,然后疯狂翻页,不久后就会跳转到这样的页面:
在这里插入图片描述
这说明大量翻页触发了网站的反爬虫措施,导致ip被封,需要进行解锁。

在这里插入图片描述
然而从doc中可以看到,请求失败的那页(状态码应该非200)被隐藏,只留下了状态码为200的这个验证页面。但是实际上,最后的那次请求返回状态码是302。

302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。

再分析一下网页的结构:
在这里插入图片描述
在搜索结果的页面中,选择标题这个元素,可以看到这篇文章的URL。点击进去后,就会跳转到微信的页面了。
在这里插入图片描述

流程框架

  • 抓取索引页内容
    利用requests请求目标站点,得到索引网页HTML代码,返回结果。

  • 代理设置(关键)

    如果遇到302状态码,则证明IP被封,切换代理重试。

  • 分析详情页内容
    请求详情页 ,分析得到标题,正文等内容

  • 将数据保存到MongoDB

爬虫实战

获取索引页

首先编写获取索引页的代码并测试:

from urllib.parse import urlencode
import  requests
from requests.exceptions import ConnectionError

base_url = 'https://weixin.sogou.com/weixin?'
headers={'Cookie': 'IPLOC=CN3205; SUID=E5C695243118960A000000005BD115E8; ld=Tlllllllll2b1SgolllllVsSVWklllllJJgpiyllll9lllllpylll5@@@@@@@@@@; SUV=1540429307839674; ABTEST=4|1541298543|v1; weixinIndexVisited=1; JSESSIONID=aaaKxagg6ZBOkf5LLDaBw; sct=2; ppinf=5|1541299811|1542509411|dHJ1c3Q6MToxfGNsaWVudGlkOjQ6MjAxN3x1bmlxbmFtZToyNzolRTclOEMlQUElRTUlODYlQjIlRTUlODclODl8Y3J0OjEwOjE1NDEyOTk4MTF8cmVmbmljazoyNzolRTclOEMlQUElRTUlODYlQjIlRTUlODclODl8dXNlcmlkOjQ0Om85dDJsdU9wakYzNVp1NklhNGhqYTdKUUxydTRAd2VpeGluLnNvaHUuY29tfA; pprdig=FzBX9Lki68sfImndi44lcV84vLEqbuPe8AXYRZYh5DtlawPVJEYr3bvv1oF8vmRfP0_rrTGYvtpqKwb39yNvJWqXl-Oh-29iaP0S893esgJdg2XNaxk7PNy5dcq1gMZOmf2kS_2YjNbV8WDULQnpjleCUcqcMMw3Av-FlSTgeh4; sgid=19-37785553-AVveXmPrwZ6BLoWTJ85UWicI; ppmdig=1541299811000000f0314597e0483df6fc4b14c41cacb024; PHPSESSID=0t7h64pmb3n0iphtp2j62i3a26; SUIR=A278AA1A3F3B46F2B8CFF48F3FD5AB76; SNUID=8ED5863612176BDC72510ED513A5E096',
'Host': 'weixin.sogou.com',
'Referer': 'https://weixin.sogou.com/weixin?query=%E7%A8%8B%E5%BA%8F%E5%91%98&type=2&page=39&ie=utf8',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
         }

#请求url,这部分需要多次使用,所以单独写为一个函数
def get_html(url):
    try :
        response = requests.get(url, allow_redirects = False,headers=headers)#禁止自动处理跳转
        if response.status_code == 200:
            return response.text
        if response.status_code == 302:
            # 需要代理
            pass
    except ConnectionError:
        return get_html(url) #若失败,重试

#获取索引页
def get_index(keyword,page):
    data = { #将get请求所需要的参数构造为字典
        'query':keyword,
        'type':2,
        'page':page
    }
    queries = urlencode(data)
    url = base_url + queries #完成url的拼接
    html = get_html(url)#调用请求页面的函数
    print(html)

if __name__ == '__main__':
    get_index('程序员',1)

上面的代码是非常好理解的。url采用拼接的方法,将前面固定的部分与后面的参数拼接起来就是我们所需要的,例如:https://weixin.sogou.com/weixin?query=程序员&type=2&page=1 这个url。
headers的部分,可以从网页审查中复制过来,并且构造为一个字典:
在这里插入图片描述
运行程序后可以看到,第一页的html已经被获取了:
在这里插入图片描述
由于只请求了第一页,并不会触发反爬虫措施,那么我们将代码完善,使程序循环地请求网页:

keyword = "程序员"
#请求url
def get_html(url):
    try :
        response = requests.get(url, allow_redirects = False,headers=headers)#禁止自动处理跳转
        if response.status_code == 200:
            return response.text
        if response.status_code == 302:
            # 需要代理
            print("302!")
    except ConnectionError:
        return get_html(url) #若失败,重试

#获取索引页
def get_index(keyword,page):
    data = { #将get请求所需要的参数构造为字典
        'query':keyword,
        'type':2,
        'page':page
    }
    queries = urlencode(data)
    url = base_url + queries #完成url的拼接
    html = get_html(url)
    return html

def main():
    for page in range(1,101):
        html = get_index(keyword,page)
        print(html)

if __name__ == '__main__':
    main()

运行后可以看到,起初还能正常请求,继而就报出了302信息:
在这里插入图片描述
这就说明此时的ip已经被封了。

使用代理

出现ip被封的情况,我们就需要更换代理进行请求。

proxy_pool_url = 'http://127.0.0.1:5000/get' #这是从web接口获取代理的地址

proxy = None #将代理设为全局变量
max_count = 5 #最大请求次数

#获取代理
def get_proxy():
    try:
        response = requests.get(proxy_pool_url)
        if response.status_code == 200:
            return response.text
        return None
    except ConnectionError:
        return None

#请求url
def get_html(url,count = 1):
    #打印一些调试信息
    print('Crawling:', url)
    print('Trying Count:', count)

    global proxy #引用全局变量
    if count >= max_count:#如果请求次数达到了上限
        print('Tried too many counts!')
        return None

    try :
        if proxy:# 如果现在正在使用代理
            proxies = {
                'http:':'http://'+ proxy #设置协议类型
            }
            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
        if response.status_code == 302:
            # 需要代理
            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:
        print('Error Occurred',e.args)#打印错误信息
        proxy = get_proxy() #若失败,更换代理
        count += 1 #请求次数+1
        return get_html(url,count) #重试

运行前需要将代理池打开,详情可参考上一篇文章:如何用Flask和Redis维护代理池
运行后:
在这里插入图片描述
可以看到程序在不断尝试使用新的代理进行请求。
但是很不幸,尝试的代理,没有一次是请求成功的…估计是这些免费的代理太多人在用了,又或者是搜狗的反爬变严格了。

解析索引页

# 解析索引页
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')

这一步很简单,通过Pyquery的标签选择器就可以完成了。

请求详情页

#请求详情页
def get_detail(url):
    try:
        response = requests.get(url)
        if response.status_code == 200 :
            return  response.text
        return None
    except ConnectionError:
        return  None

由于微信文章没有比较严格的反爬措施,也就不再需要添加代理,正常请求就好了。

解析详情页

# 解析详情页
def parse_detail(html):
    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
    }

这一步也没啥好说的,直接传标签选择器就完事儿了。

保存至MongoDB

client = pymongo.MongoClient('localhost')
db = client["weixin"]

def save_to_mongo(data):
    if db['articles'].update({'title':data['title']},{'$set':data},True):#如果不存在则插入,否则进行更新
        print('save to Mongo',data["title"])
    else:
        print('Save to Monge Failed',data['title'])

完整代码

from urllib.parse import urlencode
import  requests
from lxml.etree import XMLSyntaxError
from requests.exceptions import ConnectionError
from pyquery import PyQuery as pq
import pymongo

client = pymongo.MongoClient('localhost')
db = client["weixin"]

base_url = 'https://weixin.sogou.com/weixin?'
headers={'Cookie': 'IPLOC=CN3205; SUID=E5C695243118960A000000005BD115E8; ld=Tlllllllll2b1SgolllllVsSVWklllllJJgpiyllll9lllllpylll5@@@@@@@@@@; SUV=1540429307839674; ABTEST=4|1541298543|v1; weixinIndexVisited=1; JSESSIONID=aaaKxagg6ZBOkf5LLDaBw; sct=2; ppinf=5|1541299811|1542509411|dHJ1c3Q6MToxfGNsaWVudGlkOjQ6MjAxN3x1bmlxbmFtZToyNzolRTclOEMlQUElRTUlODYlQjIlRTUlODclODl8Y3J0OjEwOjE1NDEyOTk4MTF8cmVmbmljazoyNzolRTclOEMlQUElRTUlODYlQjIlRTUlODclODl8dXNlcmlkOjQ0Om85dDJsdU9wakYzNVp1NklhNGhqYTdKUUxydTRAd2VpeGluLnNvaHUuY29tfA; pprdig=FzBX9Lki68sfImndi44lcV84vLEqbuPe8AXYRZYh5DtlawPVJEYr3bvv1oF8vmRfP0_rrTGYvtpqKwb39yNvJWqXl-Oh-29iaP0S893esgJdg2XNaxk7PNy5dcq1gMZOmf2kS_2YjNbV8WDULQnpjleCUcqcMMw3Av-FlSTgeh4; sgid=19-37785553-AVveXmPrwZ6BLoWTJ85UWicI; PHPSESSID=0t7h64pmb3n0iphtp2j62i3a26; SUIR=A278AA1A3F3B46F2B8CFF48F3FD5AB76; SNUID=5E6D3E8EABAFD279CA76D65AAB866BC9; ppmdig=154133180100000026c4f7c59d4fcd1c4b8140c7bf7429d3',
'Host': 'weixin.sogou.com',
'Referer': 'https://weixin.sogou.com/weixin?query=%E7%A8%8B%E5%BA%8F%E5%91%98&type=2&page=10&ie=utf8',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
         }
keyword = "程序员"
proxy_pool_url ='http://127.0.0.1:5010/get' #这是从web接口获取代理的地址

proxy = None #将代理设为全局变量
max_count = 3 #最大请求次数

#获取代理
def get_proxy():
    try:
        response = requests.get(proxy_pool_url)
        if response.status_code == 200:
            return response.text
        return None
    except ConnectionError:
        return None

#请求url
def get_html(url,count = 1):
    #打印一些调试信息
    print('Crawling:', url)
    print('Trying Count:', count)

    global proxy #引用全局变量
    if count >= max_count:#如果请求次数达到了上限
        print('Tried too many counts!')
        return None

    try :
        if proxy:# 如果现在正在使用代理
            proxies = {
                'http':'http://'+ proxy #设置协议类型
            }
            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:
            print("请求成功!")
            return response.text
        if response.status_code == 302:
            # 需要代理
            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:
        print('Error Occurred',e.args)#打印错误信息
        proxy = get_proxy() #若失败,更换代理
        count += 1 #请求次数+1
        return get_html(url,count) #重试

#获取索引页
def get_index(keyword,page):
    data = { #将get请求所需要的参数构造为字典
        'query':keyword,
        'type':2,
        'page':page
    }
    queries = urlencode(data)
    url = base_url + queries #完成url的拼接
    html = get_html(url)
    if html:
        print('第'+str(page)+'页获取成功!')
        return html
    # else:
    #     return get_index(keyword,page)

# 解析索引页
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 get_detail(url):
    try:
        response = requests.get(url)
        if response.status_code == 200 :
            return  response.text
        return None
    except ConnectionError:
        return  None

# 解析详情页
def parse_detail(html):
    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


def save_to_mongo(data):
    if db['articles'].update({'title':data['title']},{'$set':data},True):#如果不存在则插入,否则进行更新
        print('save to Mongo',data["title"])
    else:
        print('Save to Monge Failed',data['title'])




def main():
    for page in range(1,101):
        html = get_index(keyword,page)
        if html:
            article_urls = parse_index(html)
            for article_url in article_urls:
                article_html = get_detail(article_url)
                if article_html:
                    article_data = parse_detail(article_html)
                    print(article_data)
                    if article_data:
                        save_to_mongo(article_data)


if __name__ == '__main__':
    main()

总结

本文主要是熟悉一下代理的设置。
需要注意的是,程序使用的代理是从自己维护的代理池中取用的(使用的是github大佬的代码),而代理池是从网上获取的一些公共IP,这些IP一般是不太稳定的,也有可能是很多人都在使用的,所以爬取效果可能不是很好,被禁的概率那是非常的高。当然,你也可以使用自己购买的一些代理,只需要改写“get_proxy”方法就ok了。
最后,还需要注意Cookies的一些设置。cookies是过期时间的,如果在抓取过程中发现无法获取后面的内容,有可能是cookies已经过期了,这时候我们重新登录一下,然后替换掉原来的Cookies就好了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

抓取微信文章:使用代理来处理反爬虫措施 的相关文章

随机推荐

  • npm,cnpm,yarn,pnmp之间的区别

    一 npm 1 由于版本号的影响 所以会造成版本不统一的情况 给定一个版本号 主版本号 次版本号 补丁版本号 以下这三种情况需要增加相应的版本号 主版本号 当API发生改变 并与之前的版本不兼容的时候 次版本号 当增加了功能 但是向后兼容的
  • Flask框架实战

    参考 第 2 章 Hello Flask Flask 入门教程 helloflask com 实战项目为一个基于flask框架实现的观影清单 个人实操后git地址 GitHub 2504973175 watchlist 基于flask框架的
  • Spark 配置

    文章目录 1 Spark 配置 1 1 Spark 属性 1 1 1 动态加载Spark属性 1 1 2 查看Spark属性 1 2 环境变量 2 重新指定配置文件目录 3 继承Hadoop集群配置 4 定制的Hadoop Hive配置 1
  • 动态白盒测试——基本路径测试法

    基本路径测试法 定义 基本路劲测试法是在程序控制流图的基础上 通过分析控制结构的环路复杂性 导出基本可执行路径集合 从而设计测试用例的方法 设计出的测试用例要保证被测程序的每个可执行语句至少被执行一次 点击学习控制流图 步骤 以详细设计或源
  • node.js连接数数据库及对数据库进行操作

    为了安装速度快一些 可以使用淘宝定制的 cnpm gzip 压缩支持 命令行工具代替默认的 npm 之后就可以使用 cnpm 命令来安装模块了 一 安装mysql驱动 cnpm install mysql 二 在工程下新建一个叫connec
  • 软件的可复用性

    1 什么是软件复用 软件复用就是利用已有的软件组件来实现或更新新的软件系统 2 软件复用的两个层面 for reuse 创造层面 开发可复用的软件 with reuse 使用层面 利用已有的可复用软件搭建新的软件应用系统 3 软件复用的优缺
  • 最简版Seq2Seq的英法机器翻译实践和详细代码解释

    Seq2Seq的英法机器翻译实践 本文的内容主要是基于英法平行语料库来实现一个简单的英法翻译模型 没有使用注意力机制和双向LSTM等技术 主要是为了掌握基本的Seq2Seq结构和TensorFlow函数使用 使用TensorFlow 1 1
  • 微信小程序预留ios安全区

    position fixed left 0 bottom 0 width 100 border top 1rpx solid ddd box shadow 0 1px 5px 0 eee background color fff z ind
  • Java使用jxl实现导出多sheet页Excel表格功能

    这篇文章主要介绍了使用jxl简化poi代码 实现导出Excel多sheet页功能 对大家的学习或者工作具有一定的参考学习价值 需要的朋友们下面随着小编来一起学习学习吧 以下代码就是一个小demo 大家可以直接自己创建maven项目然后跟着步
  • Linux Ubuntu 修改 /etc/apt/sources.list (镜像源)文件(非常实用)

    修改 etc apt sources list 文件 也即修改镜像源 能够加快在 Ubuntu 中下载和更新相关软件数据 否则默认情况下使用的是外网 下载起来比较慢 基本步骤 1 复制一份 etc apt sources list 文件 以
  • 时间序列特征构造:以电力负荷预测为例讲解(python语言)

    个人电气博文目录传送门 学好电气全靠它 个人电气博文目录 持续更新中 时间序列特征构造 时间序列问题 首先不管是回归问题 还是分类问题 一个模型的好坏 决定因素由数据集的大小 特征值的选取和处理 算法 其中最重要的是特征值的选取和处理 今天
  • 深入C++的拷贝构造和赋值函数 (深拷贝,浅拷贝)

    参考了 点击打开链接以及 高质量程序设计指南C C语言 说明 拷贝构造函数是一种特殊的构造函数 相同类型的类对象是通过拷贝构造函数来完成整个复制过程的 函数的名称必须和类名称一致 它的参数是唯一的 该参数是const类型的引用变量 例如 类
  • 尚硅谷微信小程序开发 仿网易云音乐App 小程序 后端接口服务器搭建

    目录 小程序学习 视频相关的教程文档与笔记分享 配套服务器 源码地址 接口使用说明文档 接口列表 启动服务 测试服务启动OK网页 http localhost 3000 test html 编辑 Postman测试服务器接口 postman
  • UDP协议与TCP协议的区别

    一 UDP的概述 User Datagram Protocol 用户数据报协议 UDP是传输层的协议 功能即为在IP的数据报服务之上增加了最基本的服务 复用和分用以及差错检测 二 UDP协议与TCP协议的区别 TCP连接时需要三次握手 有时
  • SparkSQL 操作数据库以及代码实践

    作者 禅与计算机程序设计艺术 1 简介 一 关于本文 SparkSQL是Apache Spark项目中用于处理结构化数据的开源模块 它提供了简单易用的API 能够将关系型数据库中的数据转换成DataFrame对象 方便进行各种分析查询 在实
  • 忘记阿里云远程连接密码怎么办

    此密码是连接终端控制台的密码 1 随便输入一个密码 点击确定 2 密码错误后网页右侧顶端出现1个修改远程连接密码的按钮 点击进去修改密码就OK了 QQ交流群 162136059
  • 二十四种设计模式之策略模式

    一 什么是策略模式 简单来说 策略模式是将每一个算法封装到拥有共同接口的不同类中 使得算法可以在不影响客户端的情况下发生变化 也可以理解为可供程序运行时选择的 不同的类 不同的解决方案 策略模式的特点 高内聚低耦合 可扩展 遵循ocp原则
  • rabbitmq 一般启动或服务启动

    rabbitmq managemen是管理后台的插件 我们要开启这个插件才能通过浏览器访问登录页面 进入到sbin目录下 rabbitmq plugins enable rabbitmq management 一般启动 服务启动 执行 ra
  • 计算机图形学---常用颜色模型汇总(RGB,CMY,HSV)

    本文整理自西安交通大学软件学院祝继华老师的计算机图形学课件 请勿转载 文章目录 常用颜色模型 RGB颜色模型 CMY颜色模型 HSV颜色模型 常用颜色模型 颜色模型 某个三维颜色空间中的一个可见光子集 包含某个颜色域的所有颜色 用途 在某个
  • 抓取微信文章:使用代理来处理反爬虫措施

    参考 崔庆才老师教程 目标网站分析 我们将从搜狗 微信这个网址来爬取微信的文章 https weixin sogou com 输入 程序员 并搜索 可以看到上方的URL有许多的信息 我们只保留query type page这几个参数即可 修