20天学会数据分析之Scrapy框架搭建Cookies池和IP代理池附案例
Cookies介绍
在浏览网站的过程中,我们经常会遇到需要登录的情况,有些页面只有登录之后才可以访问,而且登录之后可以连续访问很多次网站,但是有时候过一段时间就需要重新登录。还有一些网站,在打开浏览器时就自动登录了,而且很长时间都不会失效,这种情况又是为什么?其实这里面涉及Session和Cookies的相关知识,先来揭开Cookies的面纱。
要介绍Cookies我们必须要知道为什么需要Cookies?源于HTTP协议的无状态。
HTTP的无状态是指HTTP协议对事务处理是没有记忆能力的,也就是说服务器不知道客户端是什么状态。当我们向服务器发送请求后,服务器解析此请求,然后返回对应的响应,服务器负责完成这个过程,而且这个过程是完全独立的,服务器不会记录前后状态的变化,也就是缺少状态记录。
为了保持前后状态,我们肯定不能将前面的请求全部重传一次,这太浪费资源了,对于这种需要用户登录的页面来说,更是棘手。这时两个用于保持HTTP连接状态的技术就出现了,它们分别是Session和Cookies。
Cookies是在客户端,也可以理解为浏览器端,有了Cookies,浏览器在下次访问网页时会自动附带上它发送给服务器,服务器通过识别Cookies并鉴定出是哪个用户,然后再判断用户是否是登录状态,然后返回对应的响应。也可以理解为Cookies里面保存了登录的凭证,有了它,只需要在下次请求携带Cookies发送请求而不必重新输入用户名、密码等信息重新登录了。
因此在爬虫中,有时候处理需要登录才能访问的页面时,我们一般会直接将登录成功后获取的Cookies放在请求头里面直接请求,而不必重新模拟登录。
Cookies的属性结构
以爬取相亲网站珍爱网为例,打开首页查看存储在本地的Cookies。
Chrome浏览器中的Cookie截图,属性分别有Name、Value、Domain、Path、Expires/Max-age、Size、HttpOnly、Secure、SameSite和Priority组成。
Name和Value是一个键值对。Name是Cookie的名称,Cookie一旦创建,名称便不可更改。Value是该名称对应的Cookie的值,如果值为Unicode字符,需要为字符编码。
Domain决定Cookie在哪个域是有效的。例如,如果设置为.zhenai.com,则所有以zhenai.com,结尾的域名都可以访问该Cookie。
Path该Cookie的使用路径。如果设置为/path/,则只有路径为/path/的页面可以访问该Cookie。如果设置为/,则本域名下的所有页面都可以访问该Cookie。
Expires和Max-age均为Cookie的有效期,Expires是该Cookie被删除时的时间戳,格式为GMT。Max-age也是Cookie的有效期,但它的单位为秒,即多少秒之后失效。若Max-age设置为0,则立刻失效,设置为负数,则在页面关闭时失效。Max-age默认为 -1。
Size是此Cookie的大小。在所有浏览器中,任何cookie大小超过限制都被忽略,且永远不会被设置。
HttpOnly值为 true 或 false,若设置为true,则不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见,但在发送请求时依旧会携带此Cookie。
Secure为Cookie的安全属性,若设置为true,则浏览器只会在HTTPS和SSL等安全协议中传输此Cookie,不会在不安全的HTTP协议中传输此Cookie。
SameSite用来限制第三方 Cookie,从而减少安全风险。它有3个属性,分别是:1.Strict 严格 2.Lax 稍微放宽 3.None
Cookies以键值的方式记录会话跟踪的内容.服务器利用响应报头Set-Cookie来发送COOKIE信息.在RFC2109中定义的SET-COOKIE响应报头的格式为:
Set-Cookie: Name = Value; Comment = value; Domain = value; Max-Age = value; Path = Value;Secure; Version = 1 * DIGIT;
Cookies池介绍
平时我们在对网站进行数据抓取的时候,可以抓取一部分页面或者接口(因为毕竟网站本身须要做SEO,不会对所有网站页面都设置登陆限制),这部分可能没有设置登录限制。但是如果要抓取大规模数据的时候,没有登录进行爬取会出现一些问题。
设置了登陆限制的网站页面无法抓取。
对于一些没有设置登录的页面或者接口,一旦IP访问频繁,会触发网站的反爬虫
所以我们可以构建一个Cookies池,存储用户名和cookie的映射。Cookies池中保存了许多新浪微博账号和登陆后的Cookies信息,并且Cookies池还须要定时检测每个Cookies的有效性,如果某Cookies无效,那就删除该Cookies并模拟登陆生成新的Cookies。同时Cookies池还须要一个非常重要的接口,即获取随机Cookies的接口,Cookies运行后,咱们只需请求该接口,即可随机获得一个Cookies并用其抓取。
思路
首先我们需要多个可以登录的Cookies,然后利用这些Cookies去下载网页;一旦返回状态码不是200,就拉黑该Cookies。
具体的方法看下图:
可以使用Flask做一个API接口,每请求一次返回一个json,json中含有对应账号的Cookies。
下载CookiesPool代码GitHub:https://github.com/Python3WebSpider/CookiesPool
链接中给出的weibo的账号和密码,但是现在weibo的账号和密码登录之后需要手机验证码,需要提升此功能。或者进行其他网站的爬取也可以。
Scrapy中使用Cookies池
在项目settings.py中添加:
COOKIES_URL = 'http://127.0.0.1:5000/xxx/random'
上面提到的CookiesPool是一个基于Flask的项目,所以需要按照GitHub中的说明,进行部署启动服务器。每次发出请求获取一次Cookies。
Scrapy-Redis的配置
核心配置
首先最主要的是,需要将调度器的类和去重的类替换为 Scrapy-Redis 提供的类,在 settings.py 里面添加如下配置即可:
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
Redis 连接配置
接下来配置 Redis 的连接信息,我们可以用 Redis 的地址、端口、密码来构造一个 Redis 连接字符串,支持的连接形式如下所示:
redis://[:password]@host:port/db
password 是密码,比如要以冒号开头,中括号代表此选项可有可无,host 是 Redis 的地址,port 是运行端口,db 是数据库代号,其值默认是 0。 根据上文中提到我的 Redis 连接信息,构造这个 Redis 的连接字符串如下所示:
redis://:foobared@127.0.0.1:6379
如果没有密码则使用下面的
redis://@127.0.0.1:6379
直接在 settings.py 里面配置为 REDIS_URL 变量即可:
REDIS_URL = 'redis://:foobared@127.0.0.1:6379'
也可以分项单独配置。这个配置就更加直观明了,如根据我的 Redis 连接信息,可以在 settings.py 中配置如下代码:
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
REDIS_PASSWORD = 'foobared' # 没有password可以省略
配置调度队列
此项配置是可选的,默认使用 PriorityQueue。如果想要更改配置,可以配置 SCHEDULERQUEUECLASS 变量
scrapy_redis.queue.FifoQueue 先进先出
scrapy_redis.queue.LifoQueue 后进先出
scrapy_redis.queue.PriorityQueue (默认的)
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
还可以配置持久化,SCHEDULERPERSIST用来将Requests队列持久化到Redis,可支持暂停或重启爬虫。将 SCHEDULERPERSIST 设置为 True 之后,爬取队列和去重指纹集合不会在爬取完成后自动清空,如果不配置,默认是 False,即自动清空。
SCHEDULER_PERSIST = True
另外如果需要配置中断重爬,需要添加如下代码(这样将 SCHEDULERFLUSHON_START 设置为 True 之后,爬虫每次启动时,爬取队列和指纹集合都会清空。)
SCHEDULER_FLUSH_ON_START = True
还有一个最重要的就是配置存储目标数据库。我们采用MongoDB。配置如下
MONGO_URI = 'mongodb://用户名:密码@127.0.0.1:27017' # 如果没有用户名和密码可以省略
代理IP
代理IP到底是干什么的呢?其实代理IP可以说是一个中转站,就是你在访问互联网的时候,现在你的设备上连接IP代理,然后通过这个IP代理中转站提供的IP地址来上网。代理IP的作用也不难理解,就是用这样中转的方式来隐藏真实IP,转发请求,客户端向代理服务器发送请求,代理服务器请求到目标服务器,这样一来目标服务器收到请求后,记录的就是代理服务器的IP地址而不是真实的IP地址。整个过程下来,与目标服务器连接的都是代理服务器而非真实的客户端,从而达到了隐藏真实IP的目的。
而代理IP应用的场景也很多,例如:使用代理IP用于网络投票,使用代理IP来挖掘数据,使用代理IP进行品牌监控,舆情监控,人工智能,网络营销等等场景。
为什么需要代理
• 突破自身IP访问限制,访问一些平时不能访问的站点。
• 访问一些单位或团体内部资源:比如使用教育网内地址段免费代理服务器,就可以用于对教育网开放的各类FTP下载上传,以及各类资料查询共享等服务。
• 提高访问速度:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户再访问相同的信息时,则直接由缓冲区中取出信息,传给用户,以提高访问速度。
• 隐藏真实IP:上网者也可以通过这种方法隐藏自己的IP,免受攻击。对于爬虫来说,我们用代理就是为了隐藏自身IP,防止自身的IP被封锁。
对于我们爬虫来说,使用代理也是因为网站的反爬策略。因为现在很多网站都会有反爬虫措施,爬虫在采集过程中会发出大量的请求,使用同一个IP地址很容易触发网站的反爬虫措施,因此IP就会被限制,导致采集工作无法继续。如果想让爬虫继续下去,就需要更换IP地址,爬虫换了新的IP之后就可以继续工作了。
IP代理池
对于封IP的网站。需要很多的代理IP,去买代理IP,而对于初学者觉得没有必要,每个卖代理IP的网站有的提供了免费IP,可是又很少,写了个IP代理池 。仍然是写了一个爬虫代理,可以通过运行此代码获得更多的免费IP。
代码比较多可以回复:IP代理池获取 (注意要关注此可以哦! )
Scrapy中使用IP代理池
如果在Scrapy中使用代理池需要提起做好配置
首先启动IP代理池,添加Scrapy的中间件用户获取IP地址
def getIpList():
li=[]
global count
url ='http://127.0.0.1:8000/?types=0&count=300'
ips=requests.get(url)
for ip in eval(ips.content):
li.append(ip[0]+':'+ip[1])
return li
class ProxyMiddleware():
global count # 全局变量用于计数
count=1
global ips
ips=[]
def process_request(self, request, spider):
# Set the location of the proxy
global count
global ips
if count == 1:
ips = getIpList() # 调用函数getIPList获取IP
elif count % 100 == 0:
ips = []
ips = getIpList()
else:
pass
try:
num = random.randint(0, len(ips)) # 获取一个随机IP地址
ress = 'http://' + ips[num]
except:
pass
else:
request.meta['proxy'] = str(ress) # 添加代理IP
count += 1
在settings.py中添加此中间件
"tongcheng.middlewares.ProxyMiddleware":100,
项目案例
本次我们爬取58同城的二手房为例。本次构建一个master端和一个slave端,master端解析链接存储,slave端根据不同的请求链接获取数据。数据存储到MongoDB中。
首先创建项目和爬虫(Master)
scrapy startproject tongcheng
scrapy genspider tc_zhufang bj.58.com
from scrapy_redis.spiders import RedisSpider
from tongcheng.utils.InsertRedis import inserintotc,inserintota
import re
class TcZhufangSpider(RedisSpider):
name = 'tc_zhufang'
# 解析从start_urls下载返回的页面
# 页面页面有两个目的:
# 第一个:解析获取下一页的地址,将下一页的地址传递给爬虫调度器,以便作为爬虫的下一次请求
# 第二个:获取详情页地址,再对详情页进行下一步的解析
redis_key = 'start_urls'
def parse(self, response):
# 获取所访问的地址
response_url = re.findall('^http\:\/\/\w+\.58\.com', response.url)
detail_link_list =response.xpath('//ul[@class="house-list"]/li/@logr').extract()
next_link = response.xpath(
'//div[contains(@class,"pager")]/a[contains(@class,"next")]/@href').extract_first()
if next_link:
if detail_link_list:
inserintotc(next_link, 1)
print('*******[success] the next link ' + next_link + ' is insert into the redis queue*******')
for detail_link in detail_link_list:
detail_link = response_url[0] + '/zufang/' + detail_link.split('_')[3] + 'x.shtml'
if detail_link:
inserintota(detail_link, 2)
print('[success] the detail link ' + detail_link + ' is insert into the redis queue')
Middleware.py
from random import random
import requests
from scrapy.downloadermiddlewares.downloadtimeout import DownloadTimeoutMiddleware
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
from scrapy.exceptions import IgnoreRequest
from tongcheng.utils.message import sendMessage_warning
def getIpList():
li = []
global count
url = 'http://127.0.0.1:8000/?types=0&count=300'
ips = requests.get(url)
for ip in eval(ips.content):
li.append(ip[0] + ':' + ip[1])
return li
# Start your middleware class
class ProxyMiddleware():
global count # 全局变量用于计数
count = 1
global ips
ips = []
def process_request(self, request, spider):
# Set the location of the proxy
global count
global ips
if count == 1:
ips = getIpList() # 调用函数getIPList获取IP
elif count % 100 == 0:
ips = []
ips = getIpList()
else:
pass
try:
num = random.randint(0, len(ips)) # 获取一个随机IP地址
ress = 'http://' + ips[num]
except:
pass
else:
request.meta['proxy'] = str(ress) # 添加代理IP
count += 1
class Redirect_Middleware():
global count
count = 1
def process_response(self, request, response, spider):
# 处理下载完成的response
# 排除状态码不是304的所有以3为开头的响应
http_code = response.status
if http_code // 100 == 2:
return response
if http_code // 100 == 3 and http_code != 304:
global count
if count == 1:
sendMessage_warning()
print('302')
count += 1
# 把request返回到下载器
return request.replace(dont_filter=True)
if http_code // 100 == 4:
# 需要注意403不是响应错误,是无权访问
raise IgnoreRequest(u'404')
if http_code // 100 == 5:
return request.replace(dont_filter=True)
class RotateUserAgentMiddleware(UserAgentMiddleware):
"""
a useragent middleware which rotate the user agent when crawl websites
if you set the USER_AGENT_LIST in settings,the rotate with it,if not,then use the default user_agent_list attribute instead.
"""
# the default user_agent_list composes chrome,I E,firefox,Mozilla,opera,netscape
# for more user agent strings,you can find it in http://www.useragentstring.com/pages/useragentstring.php
user_agent_list = [ 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17',
'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)',
'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)',
'Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)',
'Mozilla/6.0 (Windows NT 6.2; WOW64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1',
'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1',
'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20120910144328 Firefox/15.0.2',
'Mozilla/5.0 (Windows; U; Windows NT 6.1; rv:2.2) Gecko/20110201',
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9a3pre) Gecko/20070330',
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.13; ) Gecko/20101203',
'Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14',
'Opera/9.80 (X11; Linux x86_64; U; fr) Presto/2.9.168 Version/11.50',
'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; de) Presto/2.9.168 Version/11.52',
'Mozilla/5.0 (Windows; U; Win 9x 4.90; SG; rv:1.9.2.4) Gecko/20101104 Netscape/9.1.0285',
'Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1.7pre) Gecko/20070815 Firefox/2.0.0.6 Navigator/9.0b3',
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080219 Firefox/2.0.0.12 Navigator/9.0.0.6',
]
def __init__(self, user_agent=''):
self.user_agent = user_agent
def _user_agent(self, spider):
if hasattr(spider, 'user_agent'):
return spider.user_agent
elif self.user_agent:
return self.user_agent
return random.choice(self.user_agent_list)
def process_request(self, request, spider):
ua = self._user_agent(spider)
if ua:
request.headers.setdefault('User-Agent', ua)
class Timeout_Middleware(DownloadTimeoutMiddleware):
def process_exception(self, request, exception, spider):
# print "####the downloader has exception!"
print(exception)
return request.replace(dont_filter=True)
配置相关
DOWNLOAD_TIMEOUT=10
DNSCACHE_ENABLED=True
#避免爬虫被禁的策略1,禁用cookie
COOKIES_ENABLED = False
CONCURRENT_REQUESTS=4
#CONCURRENT_REQUESTS_PER_IP=2
#CONCURRENT_REQUESTS_PER_DOMAIN=2
#设置下载延时,防止爬虫被禁
DOWNLOAD_DELAY = 5
#配置日志存储目录
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER_PERSIST = True
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderPriorityQueue'
REDIS_URL = None
REDIS_HOST = '127.0.0.1' # 也可以根据情况改成 localhost
REDIS_PORT = '6379'
然后启动redis,
创建slave端
scrapy startproject tongcheng_slave
scrapy genspider tczf_slave bj.58.com
spider端代码:
import re
import scrapy
from tongcheng_slave.items import TongchengSlaveItem
class TczfSlaveSpider(scrapy.Spider):
name = 'tczf_slave'
redis_key = 'tczufang_tc:requests'
def parse(self, response):
tcItem = TongchengSlaveItem()
response_url = re.findall('^http\:\/\/\w+\.58\.com', response.url)
# 字段的提取可以使用在终端上scrapy shell进行调试使用
# 帖子名称
raw_title = response.xpath(
u'//div[contains(@class,"house-title")]/h1[contains(@class,"c_333 f20")]/text()').extract_first()
if raw_title:
tcItem['title'] = raw_title.encode('utf8')
# t帖子发布时间,进一步处理
raw_time = response.xpath(
u'//div[contains(@class,"house-title")]/p[contains(@class,"house-update-info c_888 f12")]/text()').extract_first()
if raw_time:
tcItem['pub_time'] = raw_time
# 租金
tcItem['money'] = response.xpath(
u'//div[contains(@class,"house-pay-way f16")]/span[contains(@class,"c_ff552e")]/b[contains(@class,"f36")]/text()').extract_first()
# 租赁方式
raw_method = response.xpath(u'//ul[contains(@class,"f14")]/li[1]/span[2]/text()').extract_first()
if raw_method:
tcItem['method'] = raw_method
# 所在区域
try:
area = response.xpath(u'//ul[contains(@class,"f14")]/li[5]/span[2]/a[contains(@class,"c_333")]/text()').extract()[
1]
except:
area = ''
if area:
area = area
try:
area2 =response.xpath(u'//ul[contains(@class,"f14")]/li[5]/span[2]/a[contains(@class,"c_333")]/text()').extract()[
2]
except:
area2 = ''
raw_area = area + "-" + area2
tcItem['area'] = raw_area if raw_area else None
# 所在小区
try:
raw_community =response.xpath(u'//ul[contains(@class,"f14")]/li[4]/span[2]/a[contains(@class,"c_333")]/text()').extract()[
0]
tcItem['community'] = raw_community if raw_community else None
except:
tcItem['community'] = 0
# 帖子详情url
tcItem['targeturl'] = response.url
# 帖子所在城市
tcItem['city'] = response.url.split("//")[1].split('.')[0]
# 帖子的联系电话
try:
tcItem['phone'] =response.xpath(u'//div[contains(@class,"house-fraud-tip")]/div[1]/p[3]/text()').extract()[0]
except:
tcItem['phone'] = 0
# 图片1
try:
tcItem['img1'] =response.xpath(u'//ul[contains(@class,"pic-list-wrap pa")]/li[1]/@data-src').extract()[0]
except:
tcItem['img1'] = 0
# 图片2
try:
tcItem['img2'] = response.xpath(u'//ul[contains(@class,"pic-list-wrap pa")]/li[2]/@data-src').extract()[0]
except:
tcItem['img2'] = 0
yield tcItem
Items.py
# -*- coding: utf-8 -*-
#定义需要抓取存进数据库的字段
from scrapy.item import Item,Field
class TcZufangItem(Item):
#帖子名称
title=Field()
#租金
money=Field()
#租赁方式
method=Field()
#所在区域
area=Field()
#所在小区
community=Field()
#帖子详情url
targeturl=Field()
#帖子发布时间
pub_time=Field()
#所在城市
city=Field()
# 联系电话
phone= Field()
# 图片1
img1 = Field()
# 图片2
img2 = Field()
Items.py
import traceback
from pymongo import MongoClient
from scrapy.exceptions import DropItem
class TongchengSlavePipeline:
def process_item(self, item, spider):
return item
class SingleMongodbPipeline(object):
def __init__(self):
#初始化mongodb连接
try:
client = MongoClient(self.MONGODB_SERVER, self.MONGODB_PORT)
self.db = client[self.MONGODB_DB]
except Exception as e:
traceback.print_exc()
@classmethod
def from_crawler(cls, crawler):
cls.MONGODB_SERVER = crawler.settings.get('SingleMONGODB_SERVER', '101.200.46.191')
cls.MONGODB_PORT = crawler.settings.getint('SingleMONGODB_PORT', 27017)
cls.MONGODB_DB = crawler.settings.get('SingleMONGODB_DB', 'zufang_fs')
pipe = cls()
pipe.crawler = crawler
return pipe
def process_item(self, item, spider):
if item['pub_time'] == 0:
raise DropItem("Duplicate item found: %s" % item)
if item['method'] == 0:
raise DropItem("Duplicate item found: %s" % item)
if item['community']==0:
raise DropItem("Duplicate item found: %s" % item)
if item['money']==0:
raise DropItem("Duplicate item found: %s" % item)
if item['area'] == 0:
raise DropItem("Duplicate item found: %s" % item)
if item['city'] == 0:
raise DropItem("Duplicate item found: %s" % item)
# if item['phone'] == 0:
# raise DropItem("Duplicate item found: %s" % item)
# if item['img1'] == 0:
# raise DropItem("Duplicate item found: %s" % item)
# if item['img2'] == 0:
# raise DropItem("Duplicate item found: %s" % item)
zufang_detail = {
'title': item.get('title'),
'money': item.get('money'),
'method': item.get('method'),
'area': item.get('area', ''),
'community': item.get('community', ''),
'targeturl': item.get('targeturl'),
'pub_time': item.get('pub_time', ''),
'city':item.get('city',''),
'phone':item.get('phone',''),
'img1':item.get('img1',''),
'img2':item.get('img2',''),
}
result = self.db['zufang_detail'].insert(zufang_detail)
print('[success] the '+item['targeturl']+'wrote to MongoDB database')
return item
Middlewares.py同master的middlewares.py此处不再重新展现代码
Settings.py配置
# 前面的DOWNLOADER_MIDDLEWARES配置内容等同master中的settings.py的配置
.....
#单机数据库配置
SingleMONGODB_SERVER = "127.0.0.1"
SingleMONGODB_PORT = 27017
SingleMONGODB_DB = "zufang_fs"
#设置数据入库pipline
ITEM_PIPELINES = {
'tongcheng_slave.SingleMongodbPipeline':300,
}
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER_PERSIST = True
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderPriorityQueue'
REDIS_URL = None
REDIS_HOST = '127.0.0.1' # 也可以根据情况改成 localhost
REDIS_PORT = '6379'
注意:如果需要要先启动Cookies代理池和IP代理池服务器,本案例使用了ip代理池,需要提前启动
启动项目master
scrapy runspider tc_zhufang
启动slave
scrapy runspider tczf_slave
启动redis,向redis中添加start_urls
lpush start_urls https://bj.58.com/chuzu/
项目案例参考: