博客访问量v2.0 - “伪装者”版爬虫

(声明一下:刷阅读数可耻。)
上一篇文章介绍了博客爬取的思路,本文将介绍伪装的思路,尝试让爬虫模拟正常人为的操作。

思路

  1. IP代理:正常情况下,大多数的请求应来自不同IP,因此进行IP代理是十分必要的(由于没有合适的工具,因此本文不涉及IP代理);
  2. 请求头伪装:每个浏览器发出的请求头信息各不相同,特别是“Accept”、“Accept-Encoding”、“Accept-Language”、“User-Agent”等字段,通过收集各个浏览器请求头的信息,在爬虫发出请求时随机选中一个;
  3. 请求头“Referer”字段:Referer字段用于记录访问当前页面是从哪个页面跳转过来的。对于正常行为,大多是通过点击页面上的链接进入下一页面。因此,在访问文章时,应设置Referer字段的值为文章列表页的地址。
  4. Cookie:发起请求后,服务器的响应报文会携带Cookie,用于记录信息。因此启用Cookie功能,保存这些Cookie才是正常的行为。
  5. 访问文章:正常情况下,通过文章列表访问文章时,是随机访问的,所以访问顺序是乱序的,而且也不一定会把所有文章访问一遍(注:访问文章列表页也应该是随机的);
  6. 请求资源:浏览器请求一个页面后,解析页面内容,发现有js、css、图片、字体等资源需要引入,会分别发起请求去获取相应的资源(通过分析发现,CSDN网站进行资源分离,HTML文件与其它资源文件不在同一域下,因此本文未作请求资源的处理);
  7. 访问间隔:正常情况下,访问每篇文章有一定的操作间隔;
  8. 压缩格式:目前,大部分的浏览器都支持数据压缩进行传输,以优化性能。

实现

# -*-coding:utf-8-*-
"""
博客访问量
@version 2.0
@requires Python 3.6.4
@author 范围兄 <ambit_tsai@qq.com>
"""
from urllib import request
from http.cookiejar import CookieJar
from bs4 import BeautifulSoup
from time import sleep
import random
import gzip

# 博客列表
BLOG_LIST = [
    'ambit_tsai'
]
# 爬取间隔
CRAWL_INTERVAL = 50
# 请求头部列表
HEADER_LIST = [{
    # Chrome 65
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
    'Cache-Control': 'max-age=0',
    'Upgrade-Insecure-Requests': '1',
    'Connection': 'keep-alive'
}, {
    # Firefox 60
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
    'Upgrade-Insecure-Requests': '1',
    'Connection': 'keep-alive'
}, {
    # Edge 17
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134',
    'Upgrade-Insecure-Requests': '1',
    'Connection': 'keep-alive'
}, {
    # IE 11
    'Accept': 'text/html, application/xhtml+xml, image/jxr, */*',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'zh-CN',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
    'Connection': 'keep-alive'
}]

def access_article(soup, header):
    print('>>访问文章')
    tags = soup.select('#mainBox h4.text-truncate > a')
    tags = random.sample(tags, random.randrange(1, len(tags)))  # 随机抽取K个不重复的元素形成新列表
    random.shuffle(tags)                # 打乱列表顺序
    for tag in tags:
        sleep(random.randint(1, 5))     # 随机挂起1~5秒
        href = tag['href']
        print('*', href[-25:], tag.contents[2].strip())
        req = request.Request(href, headers = header)
        try:
            res = request.urlopen(req)  # 发起请求
        except Exception as ex:
            print('!', ex)
            return

def crawl_blog(blog):
    print('>>爬取博客:', blog)
    cookie_handle = request.HTTPCookieProcessor(CookieJar())
    opener = request.build_opener(cookie_handle)    # 启用Cookie
    request.install_opener(opener)
    header = random.choice(HEADER_LIST).copy()      # 随机生成请求头部
    page = 1
    while 1:
        print('>>爬取第', page, '个列表页')
        url = 'https://blog.csdn.net/'+blog+'/article/list/'+str(page)
        print('*', url)
        req = request.Request(url, headers = header)
        res = None
        try:
            res = request.urlopen(req)              # 发起请求
        except Exception as ex:
            print('!', ex)
            return
        if res.status != 200:
            print('!', res.status, '博客地址访问失败')
            return
        soup = BeautifulSoup( gzip.decompress(res.read()).decode() )    # 解析响应体
        header['Referer'] = url         # 记录博客地址到Referer,模拟正常行为
        access_article(soup, header)    # 访问文章
        if not soup.select('#pageBox'): # 判断是否没有下一页
            return
        page = page + 1                 # 下个列表页

# 主程序
while 1:
    print('=========================')
    for blog in BLOG_LIST:
        crawl_blog(blog)    # 爬取博客
    interval = CRAWL_INTERVAL + random.randint(0, 50)
    print('>>挂起', interval, '秒')
    sleep(interval)

效果

运行效果图

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值