最近有租房需求,需要整合一下各个网站的租房信息,于是想写一个脚本,定时爬取入库。

编码准备

  • scrapy
  • rds数据库
  • ip代理池

创建项目

可以使用scrapy的自带命令创建项目

# 创建项目
scrapy startproject house_need
# 进入项目目录
cd house_need
# 创建爬虫
scrapy genspider house_spider

设置文件

在编写爬虫之前,我们需要对这个项目的一些特性进行设置,下面是一些修改示例:

# ./settings.py文件修改

# 数据库信息,这里使用mysql
DATABASE_INFO = {
    "host": "",
    "port": 3306,
    "user": "user",
    "pwd": "pwd",
    "db": "house",
    "char": "utf8mb4",
}

# 是否使用代理
USE_PROXIES = True

# 代理ip 这里最好使用自动获取或者文件获取
PROXIES = [
    {
        "ip_port": "host:port",
        "user_passwd": None,
    },
]

# 一些user_agent
USER_AGENT = [
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
    "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", 
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",  
    "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",  
    "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",  
]

# 是否遵从robots.txt规则 不允许
ROBOTSTXT_OBEY = False

编写中间件和管道

上一步中我们启用了代理,并配置了数据库。在爬虫访问页面之前会调用中间件里的方法,使用代理;在爬虫爬取有效数据之后会走管道,对数据进行进一步处理,比如:清洗、入库;

编写中间件:

# ...
from house_need.settings import PROXIES, USE_PROXIES, USER_AGENT
# ...
class HouseNeedDownloaderMiddleware:
    # ...
    def process_request(self, request, spider):
        # 随机USER_AGENT
        ua = random.choice(USER_AGENT)
        print("USER_AGENT: " + ua)
        request.headers['User-Agent'] = ua
        if USE_PROXIES :
            proxy = random.choice(PROXIES)
            # 没有代理用户密码
            request.meta["proxy"] = "http://" + proxy["ip_port"]
            print("USE_PROXY: " + proxy["ip_port"])
        else :
            return None
    # ...

编写管道,这里我们只做简单的入库处理:

第一步,在items.py中编写我们所需要抓取的数据维度

class HouseNeedItem(scrapy.Item):
    # 来源
    source = scrapy.Field()
    # 标题
    title  = scrapy.Field()
    # 户型
    room   = scrapy.Field()
    # 面积
    size   = scrapy.Field()
    # 与地铁站距离
    far    = scrapy.Field()
    # 小区名称
    name   = scrapy.Field()
    # 地区
    area   = scrapy.Field()
    # 头图
    pic    = scrapy.Field()
    # 价格/月
    money  = scrapy.Field()

第二步,在pipelines.py中做入库操作,使用pymysql

from itemadapter import ItemAdapter
from house_need.settings import DATABASE_INFO
import pymysql

class HouseNeedPipeline:
    def __init__(self):
        self.db = pymysql.connect(DATABASE_INFO['host'], DATABASE_INFO['user'], DATABASE_INFO['pwd'], DATABASE_INFO['db'], charset=DATABASE_INFO['char'], port=DATABASE_INFO['port'])
        self.cursor = self.db.cursor()

    def process_item(self, item, spider):
        sql = "REPLACE INTO house_spider (subway, title, name, area, far, money, source) VALUES ('%s', '%s',  '%s',  '%s',  '%s', '%s', %d)" % ('杨家湾', item['title'], item['name'], item['area'], item['far'], item['money'], item['source'])
        print(sql)
        self.cursor.execute(sql)
        # 使用REPLACE_INTO判断返回行数,如果为1则为新数据(索引 title&source)
        if self.cursor.rowcount == 1 :
            print('新数据 发送邮件通知')
        
        self.db.commit()
        print('REPLACR INTO SUCCESSFULL')

        return item
    
    def close_spider(self, spider):
        self.cursor.close()
        self.db.close()

数据表设计如下:

CREATE TABLE `house_spider` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `subway` varchar(10) NOT NULL DEFAULT '' COMMENT '地铁站',
  `title` varchar(100) NOT NULL DEFAULT '' COMMENT '标题',
  `name` varchar(100) NOT NULL DEFAULT '' COMMENT '小区名称',
  `area` varchar(20) NOT NULL DEFAULT '' COMMENT '地区',
  `far` varchar(50) NOT NULL DEFAULT '' COMMENT '距离描述',
  `money` varchar(10) NOT NULL DEFAULT '' COMMENT '金额',
  `source` tinyint(1) NOT NULL DEFAULT '1' COMMENT '158 2安居客',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `IDX_TITLE_SOURCE` (`title`,`source`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=293 DEFAULT CHARSET=utf8mb4

第三步,在设置文件中启用中间件和管道

DOWNLOADER_MIDDLEWARES = {
   'house_need.middlewares.HouseNeedDownloaderMiddleware': 543,
}

ITEM_PIPELINES = {
   'house_need.pipelines.HouseNeedPipeline': 300,
}

至此,一些必要的设置已经完成,下一步就开始专注于爬虫编写

编写爬虫

整体浏览一下页面,发现我们需要的数据主要集中在所有的@class="house-cell realverify"下面

WX20210722-145004@2x.png

于是,根据页面结构写出相应的xpath语法:

import scrapy

from house_need.items import HouseNeedItem

class HouseSpider(scrapy.Spider):
    name = "house_spider"
    start_urls = [
        # 页面地址
        'https://wh.58.com/zufang/sub/l89/s3741/j2/',
    ]

    def parse(self, response):
        item = HouseNeedItem()
        house_div = response.xpath("//li[@class='house-cell realverify']")

        for each in house_div :
            item['source']= 1
            item['title'] = each.xpath(".//div[@class='des']//h2//a/text()").extract()[0].strip()
            item['far']   = each.xpath(".//div[@class='des']//p[@class='infor']/text()").extract()[3].strip()
            item['name']  = each.xpath(".//div[@class='des']//p[@class='infor']//a/text()").extract()[1].strip()
            item['area']  = each.xpath(".//div[@class='des']//p[@class='infor']//a/text()").extract()[0].strip()
            item['money'] = each.xpath(".//div[@class='list-li-right']//div[@class='money']//b/text()").extract()[0].strip()
            
            print(item)
            yield item

运行爬虫

在./house_need目录下运行:

scrapy crawl house_spider

WX20210722-150648@2x.png

可以看到入库正常。

安居客的页面大同小异,只需要新增spider文件即可。

Github地址:https://github.com/chation/house_spider

标签: 爬虫, 租房

添加新评论