2017/4/23 如何使爬虫更像人

作业思路

昨天在尝试了几次拉勾网的爬取,因为调试了几次后运行,然后IP就被封了
谷歌了一番,发现谷歌上还是比较少关于这方面的教程,要么是重复,要么是一笔带过,心塞……
进入正题:一个爬虫如何反Ban?

让请求更像是浏览器发出的

之前只是用了一个默认的头,这样很容易被网站给识别出来,毕竟你一个浏览器短时间发出这么多请求,非人也,所以可以联想到用很多个浏览器,这样就降低了一个浏览器发出请求的数量,看起来合理了那么些,列出很多浏览器的头,然后随机选,那头哪里来呢?这里推荐一个网站
想要更多头点我
代码在这里:
middlewares.py

# -*-coding:utf-8-*-

import random
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware


class RotateUserAgentMiddleware(UserAgentMiddleware):
  #初始化头
    def __init__(self, user_agent=''):
        self.user_agent = user_agent

  #通过random函数,随机选择头,然后伪装请求
    def process_request(self, request, spider):
        ua = random.choice(self.user_agent_list)
        if ua:
            print ua, '-----------------'
            request.headers.setdefault('User-Agent', ua)

    # 罗列出头
    user_agent_list = [
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"
        "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
    ]

setting.py

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware':None,
    'lagou.middlewares.RotateUserAgentMiddleware':400,
}

让手速慢一点

一个爬虫一秒钟请求N次,这不就是告诉别人说:“你看,我是爬虫,我的手速有多快呀”,然后ban的就是你这只爬虫,所以可以设置一下下载延迟(setting.py):
DOWNLOAD_DELAY = 1,虽然是慢一点,但是稳定,保险。

多个地点一起请求

如何来改变自己请求的地点?一般来说,我们的IP地址就是我们的地理位置标签,所以只要改变我们的IP,就可以改变我们请求的位置,随机IP请求,就能够模拟出来自全国各地的用户来访问。
所以在这里就需要用到代理IP池了,代理IP可以在通过网站上提供的,更多代理IP请点我
具体如何实现呢?

middlewares.py编写一个设置代理的类

import random
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import base64
from settings import PROXIES

class ProxyMiddleware(object):
  #编写传递请求的方法
    def process_request(self, request, spider):
        proxy = random.choice(PROXIES)
  #随机选择设置中的代理
        if proxy['user_pass'] is not None:
            request.meta['proxy'] = "http://%s" % proxy['ip_port']
            #request.meta是一个字典,包含了很多请求附加信息,这里是更改请求的IP
            encoded_user_pass = base64.encodestring(proxy['user_pass'])
            #Base64是一种基于64个可打印字符来表示二进制数据的表示方法
            request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass
            print "Successful" + proxy['ip_port']
        else:
            print "Fail" + proxy['ip_port']
            request.meta['proxy'] = "http://%s" % proxy['ip_port']
    #再换一个IP...注意是一次请求用一个有效的IP

分析:
在注释中提到request.meta,先来探究一下request.meta中到时有哪些东西,用scrapy shell测试了一下,所返回的内容是:

{'depth': 0,
 'download_latency': 16.514000177383423,
 'download_slot': 'docs.python-requests.org',
 'download_timeout': 180.0,
 'handle_httpstatus_list': <scrapy.utils.datatypes.SequenceExclude at 0x45a84b0>,
 'proxy': 'http://125.73.40.123:8118'}

从这里看,我们可以先更改请求的proxy
那么我们再来探究一下request.headers里有什么内容,同样调试,得到

{'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 'Accept-Encoding': 'gzip,deflate',
 'Accept-Language': 'en',
 'Proxy-Authorization': 'Basic ',
 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3'}

从字典上看需要修改的是proxy-authorization(因为其他的都是固定的)

setting.py添加IP

PROXIES = [
    {'ip_port': '119.5.1.38:808', 'user_pass': ''},
    {'ip_port': '115.216.31.87:8118', 'user_pass': ''},
    {'ip_port': '125.73.40.123:8118', 'user_pass': ''},
    {'ip_port': '171.38.35.20:8123', 'user_pass': ''},
    {'ip_port': '171.38.35.97:8123', 'user_pass': ''},
    {'ip_port': '222.85.39.136:808', 'user_pass': ''},
]

setting.py设置

DOWNLOADER_MIDDLEWARES = {
   'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 110,       
   'lagou.middlewares.ProxyMiddleware': 100,
}

如何来获取代理IP

上面有提供获取代理IP的网址,这里来说一下如何批量采集
编写脚本:

get_ip.py
# -*- coding: utf-8 -*-
import lxml
import requests
import re 
import sys
from bs4 import BeautifulSoup
 
 
f = open('proxy.txt' , 'w')
headers = {"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"}

for page in range(1, 101):
    link = 'http://www.xici.net.co/nn/' + str(page)
    
    html = requests.get(link, headers=headers)
    soup = BeautifulSoup(html.content, 'html.parser')
    trs = soup.find('table', id='ip_list').findAll('tr')
    for tr in trs[1:]:
        tds = tr.find_all('td')
        ip = tds[1].get_text()
        port = tds[2].get_text()
        protocol = tds[5].text.strip()
        if protocol == 'HTTP' or protocol == 'HTTPS':
            f.write('%s=%s:%s\n' % (protocol, ip, port) )
            print '%s=%s:%s' % (protocol, ip, port)
 
f.close()

但是,问题也来了,如何保证这些IP都是有用的呢?所以需要验证一下IP是否有用

验证代理IP是否有效

test_ip.py
# -*- coding: utf-8 -*-
import requests
import sys
import threading
import time
 
inFile = open('proxy.txt', 'r')
outFile = open('available.txt', 'w')

#of = open('proxy.txt' , 'w')
headers = {"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"}

lock = threading.Lock()

def test():
    while True:
        line = inFile.readline()
        if len(line) == 0: break
        protocol, proxy = line.split('=')
        try:
            proxies = {protocol: proxy}
            r = requests.get("https://www.lagou.com", headers=headers, proxies=proxies)
      #采用requests的状态码来判断
            if r.status_code == requests.codes.ok:
                print 'add proxy:' + proxy
                outFile.write(proxy + '\n')
            else:
                print  '...'

        except:
            print "fail"

#采用多线程
all_thread = []
for i in range(50):
    t = threading.Thread(target=test)
    all_thread.append(t)
    t.start()
    
for t in all_thread:
    t.join()
 
inFile.close()
outFile.close()

作业结果:

作业结果

计算了一下页面上所有的个数,这个个数只和总数相差不超过5个,相较于第一次爬取的结果为50多个好了很多。

作业中的问题

啊...这是学爬虫来研究的最久的一篇...
还有两个问题没有解决

问题一:

上面的代码

encoded_user_pass = base64.encodestring(proxy['user_pass'])
#Base64是一种基于64个可打印字符来表示二进制数据的表示方法
request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass

为什么要加encoded_user_pass = base64.encodestring(proxy['user_pass'])

问题二:

在采用了代理IP后,测试过了,正式爬取的时候,还是有一些IP会有这样的信息显示

计算机积极拒绝是什么鬼

问题三:

还是没有采用数据库的存储方式,下次改进

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • 1 前言 作为一名合格的数据分析师,其完整的技术知识体系必须贯穿数据获取、数据存储、数据提取、数据分析、数据挖掘、...
    whenif阅读 18,040评论 45 523
  • scrapy学习笔记(有示例版) 我的博客 scrapy学习笔记1.使用scrapy1.1创建工程1.2创建爬虫模...
    陈思煜阅读 12,639评论 4 46
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 山中莫道无佳味,待客潇然七碗茶 午斋时师父特别讲到了端午节,大抵意思是整个农历五月都不太好,五月端午又是最毒的一天...
    2哥不哭阅读 657评论 7 6
  • 早上醒来,发现朋友圈里被2015刷屏了。我很感动,原来2015年里他们都有许许多多的故事,或开心,或悲伤,有挑战,...
    胖子娃娃阅读 197评论 0 0