从零开始写Python爬虫 --- 2.4 爬虫实践:代理的爬取和验证

从零开始写Python爬虫 --- 2.4 爬虫实践:代理的爬取和验证

爬网站的时候,由于各种原因,ip被锁了,这个时候我们就需要通过代理来突破封锁。网上有很多代理网站,付费和免费的都有,这次我们就来写一个scrapy爬虫,爬一些免费的代理下来用。

目标分析:

本次爬取了代理网站:

目标很简单,从网上爬下ip地址和端口,保存在本地文件中


数据筛选:

相信写了这么多个demo,大家对于从html文件中定位元素位置都不陌生了吧?
我这里就不写具体是怎么抓的了,大家可以用自己的喜欢的方法,
无论是Xpath还是CSS选贼器,或者是我们原来用的最顺手的bs4

如果还是无从下手,可以回头看看我bs4爬虫的教程,看看我是如何做的
其实最重要的就是两点:

  • 掌握选择器语法
  • 熟练使用chrome开发工具

要是还是一点头绪都没有,可以之间看我下面实现的代码。


项目的创建:

这一部分很简单,就是套路东西,我也不详细说啦,
跟着代码来就行:

 # 创建项目
 scrapy startproject proxy
 # c进入项目文件夹
 cd proxy
 # 创建xici代理爬虫
 scrapy genspider dxdlspider xicidaili.com
 # 创建快代理爬虫
 scray genspider kdlspider kuaidaili.com
 
 # 看看目录长啥样
.
├── proxy
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-36.pyc
│   │   ├── items.cpython-36.pyc
│   │   ├── pipelines.cpython-36.pyc
│   │   └── settings.cpython-36.pyc
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       ├── __init__.py
│       ├── __pycache__
│       │   ├── __init__.cpython-36.pyc
│       │   ├── dxdlspider.cpython-36.pyc
│       │   └── kdlspider.cpython-36.pyc
│       ├── dxdlspider.py
│       └── kdlspider.py
└── scrapy.cfg

4 directories, 16 files

items的编写:

由于我们只需要代理的端口和ip,网页上其他花里胡哨的东西都不要,
所以items编写起来异常的简单:

import scrapy
class ProxyItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    
    #这个爬虫十分简单,我们要ip+端口,所以一个字段就够用了!
    addr = scrapy.Field()


Spider的编写:

我又要来唠叨了,这个部分还是我们scrapy框架中的核心部分!
虽然在这个例子里非常的简单:

编写大象代理的Spider:

在编写这个Spider的时候,我吃惊的发现,该站张居然提供了代理的api
api:api.xicidaili.com/free2
这也就意味着,我们不用辛苦的写代码筛选数据了,
但是我们已经把爬虫建立了,总不能删了吧?那多难受!
这个简单,我们把Response.text的内容传给item
好! 就这样做:

import scrapy
from proxy.items import ProxyItem

class DxdlspiderSpider(scrapy.Spider):
    name = "dxdlspider"
    allowed_domains = ["xicidaili.com"]
    start_urls = ['http://api.xicidaili.com/free2016.txt']

    def parse(self, response):
        item = ProxyItem()
        #因为直接调用网站的api,本身get下来的就是一个text文本,
        #我们直接把文本传给item再交给pipeline处理就行
        item['addr'] = response.text
        return item
        

编写快代理的Spider:

这个网站就没有上一个那么好了,
我们老老实实自己爬吧:

import scrapy
from proxy.items import ProxyItem


class KdlspiderSpider(scrapy.Spider):
    name = "kdlspider"
    allowed_domains = ["kuaidaili.com"]
    start_urls = []

    # 通过简单的循环,来生成爬取页面的列表
    # 这里我们爬1~5页
    for i in range(1, 6):
        start_urls.append('http://www.kuaidaili.com/free/inha/' + str(i) + '/')

    def parse(self, response):
        # 我们先实例化一个item
        item = ProxyItem()

        # 通过Xpath找到每条代理的内容
        mian = response.xpath(
            '//table[@class="table table-bordered table-striped"]/tbody/tr')

        for li in mian:
            #找到ip地址
            ip = li.xpath('td/text()').extract()[0]
            #找到端口:
            port =li.xpath('td/text()').extract()[1]
            #将两者连接,并返回给item处理
            item['addr'] = ip+':'+port
            yield item

PIPELINE的编写:

最核心和最难的爬虫部分写完了,剩下的就是收尾工作,
我们来写个PIPELINE来爬到的数据写入本地。

但是这里需要注意一下,

我们这里有两个Spider,都会往pipeline里传item
那么我们怎么分别处理呢?
我查阅了一下文档,发现并没有什么特别好的解决方式,
所以我就在pipeline里自己做了一个判断,
看代码吧:

class ProxyPipeline(object):
    '''
    这里我们通过对spider name的判断
    来分清楚item是哪一个spider传来的
    从而做出不同的处理方式
    '''
    
    def process_item(self, item, spider):
        if spider.name == 'dxdlspider':
            content = item['addr'].split('\r\n')
            for line in content:
                open('/Users/ehco/Desktop/result/dx_proxy.txt','a').write(line+'\n')


        elif spider.name=='kdlspider':
            #我们直接将传来的addr写入文本
            open('/Users/ehco/Desktop/result/kdl_proxy.txt','a').write(item['addr']+'\n')

        return item

测试一下:


啥? 为啥报错了?
这两个网站都不存在登录,
所以就不是cookie的问题
那么问题出在哪呢?
经过我仔细周密的检查,
我发现,原来是快代理有反爬虫机制:

请求过于频繁的时候,禁止访问!
上有政策,下有对策,

我们来设置一下载延迟:

DOWNLOAD_DELAY = 1

编写配置文件Settings:

BOT_NAME = 'proxy'
SPIDER_MODULES = ['proxy.spiders']
NEWSPIDER_MODULE = 'proxy.spiders'
ROBOTSTXT_OBEY = True
# 设置下载延迟,避免被封锁
DOWNLOAD_DELAY = 1
ITEM_PIPELINES = {
    'proxy.pipelines.ProxyPipeline': 300,
}

到这里,我们的功能就完全写好了

代理爬到了,

剩下的就是筛选工作了。



筛选无用代理:

网上充斥着大量的代理,但

无论是免费的还是付费的其实质量都很堪忧,

所以虽然我们从网上将代理爬了下来,

如何保证这些代理都是好用的呢?

本来我们可以把验证的过程写在PIPELINE里,
可是验证一个代理有效性的时机间,
比把数据写到本地长了十几倍,
所以我决定写一个脚本来筛选代理。

这个脚本会用到一点多线程的知识,

说实在的,Python对于多线程和多进程的支持有点差
原来我也从来没有这方面的需求,所以就一直没在意这方面
这次用到了,我也是仔细学习了一下,

下面推荐一份比较好的多线程并发材料:
一行 Python 实现并行化 -- 日常多线程操作的新思路

不多说了,看代码吧:

'''
我们通过这个小脚本来判断
抓取到的ip代理是否可以用!

还是通过我最熟悉的request库来实现
不过这里稍微加一下我也不太熟悉的多线程
'''
import requests
# 引入这个库来获得map函数的并发版本
from multiprocessing.dummy import Pool as ThreadPool
# 定义全局变量
dir_path = '/Users/ehco/Desktop/result/'
alive_ip = []

# 使得map并发!实例化pool对象
pool = ThreadPool()
# 设置并发数量!
pool = ThreadPool(20)

def test_alive(proxy):
    '''
    一个简单的函数,
    来判断通过代理访问百度
    筛选通过的代理保存到alive_ip中
    '''
    global alive_ip
    #设置代理头
    proxies = {'http': proxy}
    print('正在测试:{}'.format(proxies))
    try:
        r = requests.get('http://www.baidu.com', proxies=proxies, timeout=3)
        if r.status_code == 200:
            print('该代理:{}成功存活'.format(proxy))
            alive_ip.append(proxy)
    except:
        print('该代理{}失效!'.format(proxies))

def Out_file(alive_ip=[]):
    '''
    将符合要求的代理写入文件
    '''
    global dir_path
    with open(dir_path + 'alive_ip.txt', 'a+') as f:
        for ip in alive_ip:
            f.write(ip + '\n')
        print('所有存活ip都已经写入文件!')

def test(filename='blank.txt'):
    # 循环处理每行文件
    with open(dir_path + filename, 'r') as f:
        lines = f.readlines()
        # 我们去掉lines每一项后面的\n\r之类的空格
        # 生成一个新的列表!
        proxys = list(map(lambda x: x.strip(), [y for y in lines]))

        #一行代码解决多线程!
        pool.map(test_alive,proxys)
       
    # 将存活的ip写入文件
    Out_file(alive_ip)

#调用函数!
test('1.txt')

代理爬取结果:

因为还没到用代理的时候,我先爬取少量的:



筛选结果展示:

一共爬下来快200个,
能用的就这么点,可怜吧~


这几天我不是消失了,实在是比较忙,期末到了,各种大作业(ㄒoㄒ) 加上突发奇想,想把妹子图全站趴下来,就又浪费了一点时间。但是放心,我不会烂尾的 逃~


先放两张我爬妹子图的成果:

一共3347个图包,打包一共10g

想要资源的同学关注一下

公众号:findyourownway
回复: 妹纸图

公众号里还有免费分享的ss线路,
习惯在Google查资料的小伙伴也可以关注一波!


每天的学习记录都会 同步更新到:
微信公众号: findyourownway

知乎专栏:从零开始写Python爬虫 - 知乎专栏

blog : www.ehcoblog.ml

Github: Ehco1996/Python-crawler

编辑于 2017-05-21

文章被以下专栏收录