Skip to content

Commit

Permalink
增加了scrapy使用代理的章节
Browse files Browse the repository at this point in the history
  • Loading branch information
LianHaiMiao committed May 22, 2018
1 parent 68dd664 commit be72c68
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 3 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

## 一些库的笔记和官方文档

1. [request笔记](/python_package/request.md)
1. [requests笔记](/python_package/request.md)
2. [beautifulsoup笔记](/python_package/BeautifulSoup.md)
3. [re库官方文档](https://docs.python.org/3/library/re.html)
4. [scrapy库](https://docs.scrapy.org/en/latest/)
Expand All @@ -18,8 +18,22 @@

好的,现在开始我们的项目了...

如果了解python的语法、以及对 request库、beautifulsoup库有了一些基本了解了,那么我们就先来第一个简单的项目
如果了解python的语法、以及对 requests库、beautifulsoup库有了一些基本了解了,那么我们就先来第一个简单的项目

## requests 库

1. [python爬取股票信息(使用requests库、re库、beautifulsoup库)](/python_project/crawl_socket.md)

... ...

[TODO:补几个仅仅依靠requests、beautifulsoup小项目]

## scrapy 框架

1. [scrapy 框架简易上手]

2. [scrapy 框架使用代理(使用代理API)](/python_project/scrapy_proxy.md)


1. [python爬取股票信息(使用request库、re库、beautifulsoup库)](/python_project/crawl_socket.md)


164 changes: 164 additions & 0 deletions python_project/scrapy_proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# scrapy 使用代理

## 0.说明

因为项目需求,最近在爬取豆瓣的用户信息,爬着爬着就变成了 IP 异常.为此只能尝试使用代理池,然后免费的代理,真的打扰了...

踩了几个坑,最后决定使用 收费代理,调取API,获得代理信息.

效果还不错,于是准备把自己构建代理中间件的过程记录下来.

## 项目简介

**项目目的:**

在scrapy框架中如何根据 **付费代理** 提供的API接口去构建代理池.


**项目说明:**

很多网站,你爬取地时候,会对你的 IP 进行检测,一段时间内频繁访问,会要求你登录.或者证明自己不是机器人...

所以,为了避免陷入这样的情况,推荐几个做法

1. 使用user agent池,轮流选择之一来作为user agent。池中包含常见的浏览器的user agent(google一下一大堆) (这个效果不错,但是碰上那种,不讲道理直接封你IP的,也很倒霉)
2. 设置下载延迟(2或更高)。DOWNLOAD_DELAY = 2, 这个也真心不错.
3. 使用高度分布式的下载器(downloader)来绕过禁止(ban),您就只需要专注分析处理页面。这样的例子有: Crawlera (这个付费的,没用过,不过看了API接口,还是很方便的,但是对于国内用户来说有点小贵)
4. 使用IP池 (免费的尽量别用了吧,因为大家都在用,所以花点小钱吧).

以上四点说明采集自 [Scrapy 官方文档之实践经验(Common Practices)](http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/practices.html)


**本人购买的代理反馈结果:**

为了能够好好滴将爬虫进行下去,特意购买了代理.

调用代理API,反馈的结果如下所示:

```
{"ERRORCODE":"0","RESULT":[{"port":"33530","ip":"117.31.154.23"},{"port":"41762","ip":"171.211.53.36"},{"port":"49809","ip":"125.78.12.145"},{"port":"32762","ip":"182.42.38.170"},{"port":"24162","ip":"60.184.198.147"},{"port":"40991","ip":"171.12.176.99"},{"port":"33085","ip":"180.109.241.150"},{"port":"31207","ip":"115.237.235.238"},{"port":"44077","ip":"218.66.147.157"},{"port":"24174","ip":"113.121.246.41"}]}
```

可以看到,该 API 返回给我们的是一个 Json 串,解析之后,就是键值对的形式.



**构建代理池的思路:**

1. 调用代理API获取可以使用的IP,构建IP池

2. 验证该IP是否有效

3. 验证完IP有效,就使用该IP去完成接下来的代理任务.

4. 附加: 可以人为地增加代理IP的有效时长或者使用次数.(这里我们并没有增加这个功能,因为我碰到的爬取任务,并不需要这样...)

**注意事项:**

如果对scrapy库有疑惑可以查阅官方文档,另外对于新手,推荐使用 scrapy-cookbook 这份文档来进行入门

1. [scrapy官方文档](http://scrapy-chs.readthedocs.io/zh_CN/latest/intro/overview.html)
2. [Welcome to scrapy-cookbook](http://scrapy-cookbook.readthedocs.io/zh_CN/latest/)


## 项目部分函数的说明

> 我们会在 scrapy 框架中的 middlewares.py 文件中创建一个新的类 ProxyMiddleware ,使用这个类来完成代理操作. 同时,为了开启这个 ProxyMiddleware 中间件,需要在 setting.py 文件中做如下操作.

``` python
DOWNLOADER_MIDDLEWARES = {
'douban.middlewares.ProxyMiddleware': 543,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None,
}
```

各个函数的详细解释,都在代码中.

## scrapy框架使用代理API构建IP池

``` python
# -*- coding: utf-8 -*-
class ProxyMiddleware(object):

def __init__(self):

# 调用代理API获取IP
self.get_url = "http://api.xdaili.cn/xdaili-api//greatRecharge/getGreatIp?spiderId=6d805b68a2f44f4eb783612b242875b3&orderno=MF201852280832SEXfl&returnType=2&count=5"

# 我们使用站长工具来查看当前代理的 IP 地址.
self.temp_url = "http://ip.chinaz.com/getip.aspx"

# 我们构建的IP池,用于存储从代理API中获取的IP
self.ip_list = []

# 记录当前使用的 ip 是IP池中的哪一个
self.ip_count = 0

# 最大请求数
self.max_count = 5

def getIPData(self):
"""
使用 requests 库去调用代理API获取IP,同时,将所有的 IP 存储到 IP 池 ip_list 中.
"""
temp_data = requests.get(url=self.get_url).text
# 清空IP池
self.ip_list.clear()
# 填充IP池(这里的处理方法对于不同的代理而言,使用的函数不一样,但是本质都是一样的, 取出IP,填充)
for every_ip in json.loads(temp_data)["RESULT"]:
self.ip_list.append({
"ip": every_ip["ip"],
"port": every_ip["port"]
})

def changeProxy(self, request):
"""
更换当前的代理.
"""
request.meta["proxy"] = "http://" + str(self.ip_list[self.ip_count-1]["ip"]) + ":" + str(self.ip_list[self.ip_count-1]["port"])


def verifyIP(self):
"""
因为 IP 不是 一定有用的,有时候得到的 IP 是无效的. 所以,我们需要先测试 IP 再使用.
timeout = 3 用于设置延时,一旦超过延时就不用该 IP 了.
"""
requests.get(url=self.temp_url, proxies={"http" : str(self.ip_list[self.ip_count-1]["ip"]) + ":" + str(self.ip_list[self.ip_count-1]["port"])}, timeout=3)


# 判断这个ip是否被使用过
def verifyAndChange(self, request):
"""
:param request:
:return:
"""
try:
# 先更换代理,再验证IP
self.changeProxy(request)
self.verifyIP()
except:
# IP 校验失败,我们更换 IP,如果 IP 池中的IP 都是无效的,那么我们更换 IP 池.
if self.ip_count == 0 or self.ip_count == self.max_count:
self.getIPData()
self.ip_count = 1
# IP 校验失败,但是 IP 池中还有 IP, 更换新IP
self.ip_count += 1
self.verifyAndChange(request)

def process_request(self, request, spider):
# ip池为空,我们现在需要先获得代理IP
if self.ip_count == 0 or self.ip_count == self.max_count:
self.getIPData()
self.ip_count = 1

# IP池填充完成, 校验当前 IP 是否有用.
# 如果有效就使用,无效则更换.
self.verifyAndChange(request)

```




0 comments on commit be72c68

Please sign in to comment.