scrapy爬虫实战:安居客深圳二手房

scrapy爬虫实战:安居客深圳二手房本次爬虫实现的功能为,爬取安居客深圳二手房信息,并存入mysql数据库中。使用的是scrapy爬虫框架。1.scrapy配置:首先在cmd命令行创建项目(base)D:\python>scrapystartprojectanjukeNewScrapyproject’anjuke’,usingtemplatedirectory’D:\Anaconda3\lib\site-packages\scrapy\templates\project’,createdin:

温馨提示:想要本次爬虫源代码的同学
请关注公众号:python小咖 回复 ‘ 安居客爬虫 ’ 获取源码

—————————————————————————————————
接下来进入正题
本次爬虫实现的功能为,爬取安居客深圳二手房信息,并存入mysql数据库中。使用的是scrapy爬虫框架。

1.scrapy配置:

首先在cmd命令行创建项目

(base) D:\python>scrapy startproject anjuke
New Scrapy project 'anjuke', using template directory 'D:\Anaconda3\lib\site-packages\scrapy\templates\project', created in:
    D:\python\anjuke

You can start your first spider with:
    cd anjuke
    scrapy genspider example example.com

(base) D:\python>cd anjuke

(base) D:\python\anjuke>scrapy genspider ajk anjuke.com
Created spider 'ajk' using template 'basic' in module:
  anjuke.spiders.ajk

创建好之后就会生成一个ajk.py的文件,这里就是我们要写spider的地方。
在这里插入图片描述
此次我们抓取的信息有每个房子的标题,户型,大小,楼层,建造时间,地址,单价和总价
在这里插入图片描述
因此我们在items.py中,写入以下:
在这里插入图片描述
area,area_href,s_area,s_area_href是用来存放大区域和小区域的名字及url地址的。

2.分析网页编写代码

爬取的网址为:

https://shenzhen.anjuke.com/sale/?from=navigation

此次爬取的范围是每个区里面所有小区域的房子信息
在这里插入图片描述
分析源代码可以看到,所有区域在这个位置
在这里插入图片描述
按照每个区域进行大分类,利用xpath提取信息:

#ajk.py
a_list = response.xpath("//div[@class='div-border items-list']/div[@class='items']/span[@class='elems-l']//a")

这里的a_list是一个列表,里面是上图中span标签里的所有a标签
我们遍历它获取每个区域的名字和href,这是我们的第一个parse:

#ajk.py
    def parse(self, response):
        a_list = response.xpath("//div[@class='div-border items-list']/div[@class='items']/span[@class='elems-l']//a")

        for a in a_list:
            item = AnjukeItem()
            item['area'] = a.xpath("./text()").extract_first()
            item['area_href'] = a.xpath("./@href").extract_first()

            yield scrapy.Request(
                item['area_href'],
                callback=self.sarea_parse,
                meta={ 
   'item':item}
            )

然后通过yield构造请求,访问到每个区的页面,然后进一步获取每个区里面的小区域的地址。
下面开始分析小区域的源代码
在这里插入图片描述
我们可以发现小区域在

#ajk.py
div[@class='div-border items-list']/div[@class='items']//div[@class='sub-items']

的a标签里面,接着通过xpath提取数据

#ajk.py
a_list = response.xpath("//div[@class='div-border items-list']/div[@class='items']//div[@class='sub-items']//a")

和上面大区域一样,接着遍历这一个列表,获得小区域的名字和地址,并写在sarea_parse函数中

#ajk.py
    def sarea_parse(self,response):
        item = response.meta.get('item')
        a_list = response.xpath("//div[@class='div-border items-list']/div[@class='items']//div[@class='sub-items']//a")
        for a in a_list:
            item['s_area'] = a.xpath("./text()").extract_first()
            item['s_area_href'] = a.xpath("./@href").extract_first()
            yield scrapy.Request(
                item['s_area_href'],
                callback=self.parse_datail,
                meta={ 
   'item':deepcopy(item)}
            )

然后就可以通过yield构造请求,去到小区域的各个页面中,就可以开始提取房子的信息了。

下面开始分析我们要提取的信息的源代码

通过分析网页源码可以发现,我们需要的详情列的信息,全都在一个id为”houselist-mod-new”的ul标签里面,每个房子信息在一个个的 li 里面。
在这里插入图片描述
li 的结构如下:
在这里插入图片描述
我们要的全部信息都在这里面。
然后,就可以开始写代码了,代码如下:

#ajk.py
    def parse_datail(self, response):
        item = response.meta.get('item')
        li_list = response.xpath("//div[@class='sale-left'][1]/ul[@id='houselist-mod-new']//li")
        for li in li_list:
            item['title'] = li.xpath("./div[@class='house-details']/div[@class='house-title']/a/text()").extract_first().strip()
            item['unitType'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][1]//span[1]/text()").extract_first()
            item['size'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][1]//span[2]/text()").extract_first()
            item['floor'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][1]//span[3]/text()").extract_first()
            item['build_date'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][1]//span[4]/text()").extract_first()
            item['address'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][2]/span/@title").extract_first().strip().replace('\xa0','-')
            item['unitprice'] = li.xpath("./div[@class='pro-price']/span[@class='unit-price']/text()").extract_first()
            item['price'] = li.xpath("./div[@class='pro-price']/span[@class='price-det']/strong/text()").extract_first() + '万'

            yield item

写到这里,我们的爬虫就基本完成了。
当然这里只是一页的内容,我们看到页面的最下面,下一页按钮那里:
在这里插入图片描述
就这分析这部分的网页源码:
在这里插入图片描述
下一页的按钮在一个class=’multi-page’的a标签下面,接着通过xpath定位到这里

next_page = response.xpath("//div[@class='sale-left'][1]/div[@class='multi-page']/a[@class='aNxt']/@href").extract_first()

然后在加上一个小小的判断,当下一页不为空的时候,也就是不是最后一页的时候才构造请求,callback还是parse_datail,完整代码如下

#ajk.py
    def parse_datail(self, response):
        item = response.meta.get('item')
        li_list = response.xpath("//div[@class='sale-left'][1]/ul[@id='houselist-mod-new']//li")
        for li in li_list:
            item['title'] = li.xpath("./div[@class='house-details']/div[@class='house-title']/a/text()").extract_first().strip()
            item['unitType'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][1]//span[1]/text()").extract_first()
            item['size'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][1]//span[2]/text()").extract_first()
            item['floor'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][1]//span[3]/text()").extract_first()
            item['build_date'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][1]//span[4]/text()").extract_first()
            item['address'] = li.xpath("./div[@class='house-details']//div[@class='details-item'][2]/span/@title").extract_first().strip().replace('\xa0','-')
            item['unitprice'] = li.xpath("./div[@class='pro-price']/span[@class='unit-price']/text()").extract_first()
            item['price'] = li.xpath("./div[@class='pro-price']/span[@class='price-det']/strong/text()").extract_first() + '万'

            yield item

        next_page = response.xpath("//div[@class='sale-left'][1]/div[@class='multi-page']/a[@class='aNxt']/@href").extract_first()
        if next_page is not None:
            yield scrapy.Request(
                next_page,
                callback=self.parse_datail,
                meta={ 
   'item':item}
            )

3.数据放入数据库

在scrapy中,我们是在pipeline中处理数据的,因此写入数据库的操作我们也在pipeline中进行。
在写入数据库之前,我们先加上一个日志显示功能,这样在后面的操作中,我们可以看到我们想要看到的信息。

#pipelines.py
import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s-(%(filename)s)-[line:%(lineno)d]-%(levelname)s : %(message)s',
                    filename='logging.log',
                    datefmt='[%d/%b/%Y %H:%M:%S]')

logger = logging.getLogger(__name__)

接着我们在setting.py中将log的等级设置为warning,这样,就可以只看到warning等级以上的日志了

LOG_LEVEL = 'WARNING'

然后我们写入数据库的操作在class AnjukePipeline 中进行,在操作开始之前,首先我们要先了解到两个函数 open_spider(self, spider)和close_spider(self, spider)。

open_spider(self, spider) 它会在开启爬虫时执行,且只执行一次
而close_spider(self, spider)则是在爬虫关闭时时执行,且只执行一次。
所以我们的连接数据库操作和关闭数据库操作就可以放在这两个函数里面,
具体代码如下:

class AnjukePipeline:
	def open_spider(self, spider):
	    self.connection = pymysql.connect(
	        host='localhost',
	        user='root',
	        password='yang4869',
	        database='house',
	        charset='utf8'
	    )
	    self.cursor = self.connection.cursor()
	    logger.warning('database create succeeded')
	def close_spider(self, spider):
	    self.cursor.close()
	    self.connection.close()
	    logger.warning('database close succeeded')

然后我们的写入数据库操作就放在process_item(self, item, spider)中执行,它会在接受到来自spider的数据后自动执行。
结合上面的open_spider和close_spider后,完整代码如下:

#pipelines.py
    class AnjukePipeline:
    	number = 0
	    def open_spider(self, spider):
	        self.connection = pymysql.connect(
	            host='localhost',
	            user='root',
	            password=你自己的密码,
	            database='house',
	            charset='utf8'
	        )
	        self.cursor = self.connection.cursor()
	        logger.warning('database create succeeded')
	
	    def process_item(self, item, spider):
	        try:
	            sql = '''insert into ajk (area,sarea,area_href,sarea_href,unittype,size,floor,build_date,address,unitprice,price,title) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'''
	            self.cursor.execute(sql,
	                                (item['area'], item['s_area'], item['area_href'],item['s_area_href'],item['unitType'],
	                                 item['size'],item['floor'],item['build_date'],item['address'],item['unitprice'],item['price'],item['title']))
	            self.connection.commit()
	            self.number += 1
	            logger.warning("第{0}条房子----'{1}'信息----抓取成功!".format(self.number,item['title']))
	        except pymysql.DatabaseError as e:
	            logger.warning('pipeline:' + str(e))
	
	        return item
	
	    def close_spider(self, spider):
	        self.cursor.close()
	        self.connection.close()
	        logger.warning('database close succeeded')

最后记得在setting.py中打开pipeline:

ITEM_PIPELINES = { 
   
   'anjuke.pipelines.AnjukePipeline': 300,
}

为了应付反爬,我们也可以在setting.py中加入USER_AGENT,并且把延时给设置上去(DOWNLOAD_DELAY),因为我也不急着拿数据,我的延时就直接设置了一个5s,但是要注意的是,DOWNLOAD_DELAY 会影响 CONCURRENT_REQUESTS,不能使并发显现出来。
除了加入USER_AGENT和设置DOWNLOAD_DELAY ,还要禁用一下cookie

COOKIES_ENABLED = False

当然也可以在中间件中设置随机User-Agent,在这里我就不过多介绍了。

运行爬虫后,我总共爬取到了23w条数据。

mysql> use house;
Database changed
mysql> select count(*) from ajk;
+----------+
| count(*) |
+----------+
|   230962 |
+----------+
1 row in set (5.16 sec)


最后!!!
scrapy框架我也是新手,有什么不对的地方欢迎指出。

最后想要本次爬虫源代码的同学

请关注公众号:python小咖 回复 ‘ 安居客爬虫 ’ 获取源码

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/38277.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注