Scrapy基于CrawlSpider进行全站数据爬取
前言:CrawlSpider是基于Spider类的一个子类,其主要用作于全站数据爬取。
本次训练的网站是【链家】,使用CrawlSpider进行全站数据爬取,厦门的租房-整租信息。(这个训练也不算全站啦~哈哈哈😂)
点击下载【完整代码】
一.前期工作
1.创建项目:scrapy startproject 项目名称
2.cd
进入项目
3.创建爬虫文件:scrapy genspider -t crawl 爬虫文件名 网站域名(xxx.com)
4.修改配置文件setting.py
,取消注释USER_AGENT
,修改成自己的;关闭遵循robots协议
;推荐添加LOG_LEVEL = "WARNING"
也就是设置输出日志LOG。
二、剖析爬虫文件
剖析CrawlSpider类的爬虫文件。
其他的跟以前Spider的没区别,有区别的如下
'''
在rules中包含一个或多个Rule对象
LinkExtractor(allow=r'...'):链接提取器,根据指定规则(allow=r'正则')进行指定链接的提取
Rule(...):规则解析器,将链接提取器提取到的链接进行指定规则(callback)的解析操作
follow=True:可以将链接提取器继续作用到链接提取器提取到的链接所对应的页面中
'''
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
三、了解需求
🌈需求:使用CrawlSpider,爬取厦门的所有租房-整租信息。
四、分析网站
了解以上知识跟需求后,那就打开这网站来看看吧。
打开网站,我们要弄清楚,我们需要爬取那些信息?这些信息分别在哪些有页面?所涉及的url有哪些?而这些url之间又有什么规律?
1.打开网站,简单的浏览下,发现,我们需要爬取的数据并不在一个页面。
2.我们了解了需要爬取的数据之后,我们看看所涉及的url,F12检查网页,先看每页的url,定位到页码按钮,查看其每页的url,寻找规律。
多检查几页的url,我们不难发现,第一页的url为:/zufang/rt200600000001/#contentList
,后面的,第几页,参数pg后面就跟着数字几,如第二页,url就为/zufang/pg2rt200600000001/
,并且发现省略掉其末尾的#contentList
返回的响应内容是一样的。
3.我们再看看详情页面的url之间的关系,
原来如此,知道这些信息之后,那我们开始动手吧!
五、编写代码
1.打开爬虫文件,修改start_urls
。
start_urls = ['https://xm.lianjia.com/zufang/rt200600000001/']
2.根据上面我们所总结掌握的,编写rules
。
rules = (
# 每页
Rule(LinkExtractor(allow=r'(/zufang/pg\d+rt200600000001/$|/zufang/rt200600000001/$)'), callback='parse_item',follow=True),
# 详情
Rule(LinkExtractor(allow=r'/zufang/XM\d+.html$'), callback='parse_detail'),
)
【注释】
为什么在详情 规则解析器中,没有添加follow=True
,因为我们进入详情页面获取所需数据就行了,并不需要再作用到其他页面。callback
,获取的响应数据回调到指定的方法,这里也就是parse_detail
。所以在下面,我们需要定义一个同名
的方法,
3.使用xpath爬取所需数据
代码如下:
# 每页url的响应内容
def parse_item(self, response):
# print(response)
item = RentingItem()
try:
item['house_code'] = response.xpath('//*[@id="content"]/div[1]/div[1]/div[@data-event_send="no"]/@data-house_code').extract_first() # 房屋编号
item['region'] = "-".join(response.xpath('//*[@id="content"]/div[1]/div[1]/div[1]/div/p[2]/a/text()').extract()) # 地区
yield item
except Exception as e:
# 如果在这里报错,先看打印日志,是否是被链家封了IP
# 如果打印的url,点进去能正常访问的话,再查看自己的xpath是否有写错。
# 此处建议使用代理IP爬取
print(f'{response.url}数据爬取失败:{e}')
# 每页详情url的响应内容
def parse_detail(self, response):
# print(response)
item = DetailItem()
try:
item['house_id'] = response.xpath('/html/body/div[3]/div[1]/div[3]/div[1]/i/text()').extract_first().split(':')[-1] # 房间ID
item['title'] = response.xpath('/html/body/div[3]/div[1]/div[3]/p/text()').extract_first() # 标题
item['price'] = response.xpath('//*[@id="aside"]/div[1]/span/text()').extract_first() # 价格
item['lease'] = response.xpath('//*[@id="aside"]/ul/li[1]/text()').extract_first() # 租赁方式
item['house_type'] = response.xpath('//*[@id="aside"]/ul/li[2]/text()').extract_first() # 房屋类型
item['towards'] = response.xpath('//*[@id="aside"]/ul/li[3]/span[2]/text()').extract_first() # 朝向楼层
yield item
except Exception as e:
# 同上
print(f'{response.url}数据爬取失败:{e}')
【注释】
我们会发现这两个方法并没任何关联,各做各的事,也就是说,数据不能像之前一样能通过meta
来保持多个请求之间的数据连接。他们提交的item
都是各自独立的。所以,上面我们看到实例化了两个不同的item对象。
4.先设置爬取字段,声明数据模型
class RentingItem(scrapy.Item):
house_code = scrapy.Field() # 房屋编号
region = scrapy.Field() # 地区
class DetailItem(scrapy.Item):
house_id = scrapy.Field() # 房屋编号
title = scrapy.Field() # 标题
price = scrapy.Field() # 价格
lease = scrapy.Field() # 租赁方式
house_type = scrapy.Field() # 房屋类型
towards = scrapy.Field() # 朝向楼层
5.到这,我们就拿到了我们想要的字段,但是问题来了,我们把item
提交给管道,管道怎么知道我们是哪个方法提交过来的
所以需要分情况来写:
def process_item(self, item, spider):
if item.__class__.__name__ == 'RentingItem':
# print(item['house_code'], '>> RentingItem')
pass
else:
# print(item['house_id'], '>> DetailItem')
pass
return item
问题又来了,我们怎么做到数据的统一性。房屋编号
用房屋编号做外键
,这也说明了,刚才我们为什么再爬取数据的时候,在详情页面获取了房屋编号,在外面也获取了房屋编号。
好了,数据也获取到了,管道也接收到了,最后一件事就是,开启管道
。在setting.py
文件中,取消注释ITEM_PIPELINES
。
六、数据持久化存储
关于数据的存储,在文章《Scrapy中管道类的使用及如何将数据存储到MySQL数据库》 也做了讲解,就不啰嗦了,如果有需要的,点击下载【完整代码】进行查看。
需要注意的点,在设计数据库时,需要将房间编号
设置为主键。
七、拓展
在爬取链家时,被封IP
机率,老高了,我这里使用的是,伪装UA+代理IP,才能顺利的爬取下来。关于伪装UA跟使用代理IP,我的这篇文章《Scrapy使用伪装术》,有做讲解。
新手上路,代码写得不好,如果有理解错误的,还请大佬在评论区指出来,非常感谢!
以上就是使用CrawlSpider进行全站数据爬取练习的所有内容了,点赞收藏加评论是最大的支持哦!
📑编写不易,转载请注明出处,如有侵权,请联系我!