学习目标:
Python学习二十七—简单数据抓取七
学习内容:
1、采取蘑菇API代理设置scrapy的代理IP池并利用redis形成队列依次使用
2、利用ip池访问网页并将scrapy爬取转移到items的数据存入到数据库
3、注意事项
1、采取蘑菇API代理设置scrapy的代理IP池并利用redis形成队列依次使用
- 以安居客为例(安居客会根据访问的IP地址对用户进行限制)
1、首先创建一个爬取安居客全站的项目 - 在cmd窗口cd到项目需要保存的位置,输入:scrapy startproject fangzi,创建fangzi项目
- cd到fangzi项目里面,然后输入:scrapy genspider anjuke tianjin.anjuke.com/sale/p1/?from=navigation,创建anjuke爬虫
- 创建run运行文件
2、创建利用蘑菇代理API代理获取IP地址池的get_ip.py文件
import time
import requests
import redis
# 在死循环中不断获取API代理的IP地址
while True:
# 连接redis数据库
r = redis.Redis(host='127.0.0.1', port=6379, db=0)
# 判断数据库的IP个数是否满
if r.llen('proxy_ip') < 10:
# 获取API链接中信息,并且转换为json数据
source = requests.get('蘑菇代理的API链接').json()
# 根据获取的API信息,拿出需要的IP地址和port端口
for i in source['msg']:
# 按照格式将两个信息进行拼接
ip = i['ip'] + ':' + i['port']
print(ip)
# 插入到redis数据库中
r.lpush('proxy_ip', ip)
else:
# 延迟3秒,之后打印IP池的状态
time.sleep(3)
print('代理IP池已满!!!')
3、在middlewares.py文件中进行相关的请求与返回的配置
- middlewares文件中需要连接数据库,在最前端输入如下:
import redis
r = redis.Redis(host='127.0.0.1', port=6379, db=0)
- middlewares文件中网页访问链接的时候带上proxy,找到process_request输入如下:
def process_request(self, request, spider):
# ip地址从redis数据库中取出,并转码为utf8
ip = r.lpop('proxy_ip').decode('utf8')
print(ip)
# 请求链接时带上该proxy
request.meta['proxy'] ='https://' + ip
return None
- middlewares文件中网页访问返回的信息,在response中进行处理,找到process_response输入如下:
def process_response(self, request, response, spider):
# Called with the response returned from the downloader.
# 判断返回信息是否为200正常状态
if response.status == 200:
# 同时根据实际页面的大小为700判断是否返回的是正常页面,可以在请求的页面上用len方法获取到大小
if len(response.text) > 750000:
# 将正常请求网页的链接进行分割取出ip和port回收到redis数据库
ip = request.meta['proxy'].split('//')[-1]
# 将正常能用的ip和port重新插回到redis数据库
r.lpush('proxy_ip', ip)
# 并返回正常的信息
return response
# 重回调度队列
return request
- middlewares文件中如果请求不到正确的网页信息,就到process_exception,重新获取新的IP进行新的请求,输入如下:
def process_exception(self, request, exception, spider):
# Called when a download handler or a process_request()
# (from other downloader middleware) raises an exception.
ip = r.rpop('proxy_ip').decode('utf8')
print(ip)
request.meta['proxy'] = 'https://' + ip
# Must either:
# - return None: continue processing this exception
# - return a Response object: stops process_exception() chain
# - return a Request object: stops process_exception() chain
return request
4、在setting.py文件中,进行相关的配置
- 请求下载页面内容的延迟
DOWNLOAD_DELAY = 2
- 打开代理IP的设置
DOWNLOADER_MIDDLEWARES = {
'fangzi.middlewares.FangziDownloaderMiddleware': 543,
}
2、利用ip池访问网页并将scrapy爬取转移到items的数据存入到数据库
1、利用设置好的IP地址池,来请求安居客网页避免因为同一个IP地址频繁访问而被封掉
import scrapy
from fangzi.items import FangziItem
class AnjukeSpider(scrapy.Spider):
name = 'anjuke'
# 利用代理ip以及携带header头访问安居客网页,并且关系到下面的get_ip方法,start_requests方法获取的东西都传给get_ip方法
def start_requests(self):
yield scrapy.Request('https://tianjin.anjuke.com/sale/p1/?from=navigation', self.first, headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36'
}, dont_filter=True)
# 从安居客二手房的第一页获取到所有页面的信息,然后抓取全站的房源信息
def first(self, response):
one_type = response.xpath('//*[@id="__layout"]/div/section/section[2]/section/div[1]/section/ul[2]/li/a/@href').extract()[1:]
for hrefs in one_type:
yield scrapy.Request(hrefs, self.get_href)
def get_href(self, response):
two_type = response.xpath('//*[@id="__layout"]/div/section/section[2]/section/div[1]/section/ul[3]/li/a/@href').extract()[1:]
for hrefs in two_type:
yield scrapy.Request(hrefs, self.get_page)
def get_page(self, response):
max_page = response.xpath('//li[@class="page-item last"]/a/text()').extract_first('1')
for page in range(1, int(max_page)+1):
last_href = response.url.replace('/?', '/p'+str(page)+'/?')
yield scrapy.Request(last_href, self.get_ip)
def get_ip(self, response):
# 实例化item购物车
item = FangziItem()
# 提取xpath的公共部分
base = response.xpath('//div[@class="property"]')
for i in base:
# xpath到房源信息的标题
title = i.xpath('a/div[2]/div[1]/div[1]/h3/text()').extract_first()
# xpath到房源信息的房型
house_type = ''.join(i.xpath('a/div[2]/div[1]/section/div[1]/p[1]/span/text()').extract())
# xpath到房源信息的面积
house_size = i.xpath('a/div[2]/div[1]/section/div[1]/p[2]/text()').extract_first().strip()
print(title, house_type, house_size)
# 将获取的三个信息合并成列表,并根据','分割
lists = 'title,house_type,house_size'.split(',')
# 将获取的内容插入到item中,这里用了一个eval的方法
for i in lists:
item[i] = eval(i)
yield item
- 同时item.py文件的内容
import scrapy
class FangziItem(scrapy.Item):
title = scrapy.Field()
house_type = scrapy.Field()
house_size = scrapy.Field()
2、将scrapy项目连接到数据库,并将获取的信息存入到数据库中
- 在settings.py文件中进行数据库相关信息的配置,在最后输入如下:
DB_USER = 'root'
DB_PWD = '1234'
HOST = '127.0.0.1'
DB_NAME = 'anjuke'
- 记得将setting文件中的打开
ITEM_PIPELINES = {
'fangzi.pipelines.FangziPipeline': 300,
}
- 在pipliens.py文件中获取item购物车中的信息,并存入到mysql数据库中:
import pymysql
class FangziPipeline(object):
# 初始化相关的数据库配置信息
def __init__(self, user, password, host, db_name):
self.user = user
self.password = password
self.host = host
self.db_name = db_name
# 利用修饰符在不形成实例化的情况下直接调用settings文件中数据库配置信息
@classmethod
def from_crawler(cls, crawler):
return cls(user=crawler.settings.get('DB_USER'), password=crawler.settings.get('DB_PWD'), host=crawler.settings.get('HOST'), db_name=crawler.settings.get('DB_NAME'))
def open_spider(self, crawler):
# 该方法继承类的默认属性,并得到数据库配置信息,连接到mysql数据库
self.db = pymysql.connect(user=self.user, password=self.password, host=self.host, db=self.db_name, charset='utf8')
# 测试数据库的连通性
self.db.ping(reconnect=True)
# 使用cursor()方法获取数据库的操作游标
self.cursor = self.db.cursor()
def process_item(self, item, spider):
# 将item中的数据插入到设置好的mysql数据库中
sql = "insert into anjuke(title, house_type, house_size) VALUE ('{}', '{}', '{}')".format(item['title'], item['house_type'], item['house_size'])
# 执行该插入数据命令
self.cursor.execute(sql)
# 提交执行
self.db.commit()
return item
def close_spider(self, spider):
# 关闭数据库
self.db.close()
3、注意事项
1、ip池利用了redis数据库
2、获取的数据存储利用了mysql数据库
3、运行时,首先运行get_ip.py文件,方便获取新鲜的ip地址,再运行run.py文件
4、抓取安居客全站的信息时,注意各个分类以及相关最大页数的提取