您当前的位置: 首页 >  爬虫

奇巧小软件

暂无认证

  • 4浏览

    0关注

    16博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

爬虫15:反“反爬“

奇巧小软件 发布时间:2022-10-13 17:59:53 ,浏览量:4

主题一:反爬

为什么爬虫不用太担心禁止IP的反爬方法(顶多封几个小时IP,我们也有对应的方案),原因如下:

1.比方说一个学校/网吧,对外的IP只有一个或者几个,大家都通过局域网对外访问,如果因为学校里面的学生写了一个爬虫而把公网IP封了,那么整个学校1万多人都无法访问该的网站,损失量巨大

2.IP是动态分配的,如果将IP禁止访问,当把此IP分配给其他"无辜的人"时,他就无法登陆你的网站

信心来源:理论上,网站是不可能从技术上禁止爬虫,最后肯定是爬虫获胜的 alt

主题二:scrapy架构图 alt

看图中的序号和主件。

  • SPIDER:爬虫;1.用于yield出各个请求;2.定义回调函数处理请求下载的response,提取数据;3.对应提取出来的数据yield出item

  • ENGINE:引擎,理解成调度机制(大总管),当序号1来的请求进入到ENGINE,就会被识别为请求并经过序号2被放到调度器中。

  • SCHEDULER:调度器,涉及爬取顺序:队列(广度优先)、堆栈(深度优先),将准备好的请求一个个丢给ENGINE(序号3)

  • DOWNLOADER:下载器,下载网页内容。途径4和5是下载中间件,对请求和响应做拦截,比如对请求统一的加头、加代理或者集成selenium自己爬取。经过序号5回到ENGINE中,被ENGINE识别为响应,就回到SPIDERS中做解析。

  • ITEM PIPELINES:管道,负责数据的清洗和入库

主题三:针对反"反爬"的几个注意点 1.如何随机的更换user-agent(用户-代理)

首先,scrapy提供了一个默认的UserAgentMiddleware(源码),用于从配置文件中读取USER_AGENT,给request的请求头设置上

但是如果老是用同一个USER_AGENT去爬,则很容易被反爬,所以有了更换请求头的中的USER_AGENT。

要使用我们自己的中间件来实现随机变换user-agent,就需要将scrapy默认的这个中间件置为None,并配置上自己写的middlewares.

alt

具体如下:在scrapy项目新建之初,项目目录下就有一个middlewares.py的文件用于编写自定义的中间件。

alt

process_request()请求来的时候做拦截,它有三种类型的返回值

  • return none:程序继续往后执行
  • return response对象:不走downloader,直接把响应对象返回给engine,然后给spider做解析
  • return request对象:不走downloader,直接把请求对象返回给engine,然后丢到调度器中排序继续来发请求
  • raise异常:流程转到 process_exception方法中处理(常用于拦截不需要下载的url)
#request.headers是一个Headers对象,继承至字典print(type(request.headers))print(request.headers)from scrapy.http.headers import Headers# 给USER_AGENT随机换值,也可以这么写request.headers['User-Agent']='UserAgent().random'
2.IP的变化方案:设置IP代理 alt

在刚刚上面的中间件的process_request()末尾加上代理,语句就一句,如下:

request.meta["proxy"] = 'http://IP地址:端口'

但是不能只用1个IP,而需要一个IP代理池

百度中搜索IP代理,将高匿的IP代理爬取下来放在数据库中,每次随机取一条即可,专门写一个工具来处理,如下:

import requestsfrom scrapy.selector import Selectorimport MySQLdbconn = MySQLdb.connect(host="127.0.0.1", user="root", passwd="root", db="article_spider", charset="utf8")cursor = conn.cursor()def crawl_ips():    #爬取西刺的免费ip代理    headers = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0"}    for i in range(1568):        re = requests.get("http://www.xicidaili.com/nn/{0}".format(i), headers=headers)        selector = Selector(text=re.text)        all_trs = selector.css("#ip_list tr")        ip_list = []        for tr in all_trs[1:]:            speed_str = tr.css(".bar::attr(title)").extract()[0]            if speed_str:                speed = float(speed_str.split("秒")[0])            all_texts = tr.css("td::text").extract()            ip = all_texts[0]            port = all_texts[1]            proxy_type = all_texts[5]            ip_list.append((ip, port, proxy_type, speed))        for ip_info in ip_list:            cursor.execute(                "insert proxy_ip(ip, port, speed, proxy_type) VALUES('{0}', '{1}', {2}, 'HTTP')".format(                    ip_info[0], ip_info[1], ip_info[3]                )            )            conn.commit()class GetIP(object):    def delete_ip(self, ip):        #从数据库中删除无效的ip        delete_sql = """            delete from proxy_ip where ip='{0}'        """.format(ip)        cursor.execute(delete_sql)        conn.commit()        return True    def judge_ip(self, ip, port):        #判断ip是否可用        http_url = "http://www.baidu.com"        proxy_url = "http://{0}:{1}".format(ip, port)        try:            proxy_dict = {                "http":proxy_url,            }            response = requests.get(http_url, proxies=proxy_dict)        except Exception as e:            print ("invalid ip and port")            self.delete_ip(ip)            return False        else:            code = response.status_code            if code >= 200 and code             
关注
打赏
1661325404
查看更多评论
0.0379s