房天下房源爬取:scrapy-redis分布式爬虫(一)

2023-10-24 23:10

本文主要是介绍房天下房源爬取:scrapy-redis分布式爬虫(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

从前几天就开始准备写一个简单地分布式爬虫的项目了,今天算是把问题和bug逐渐解决了,所以会陆续放上来。

主机是windows即此电脑,项目也是在windows下写的,运行会放在linux下的ubuntu系统。

项目是爬取房天下网站全国600多个城市的所有新房和二手房信息

今天主要记录一下windows下项目的完成,和数据库存储。新房存储在MySQL数据库,二手房存储在mongo数据库。

这个网站没有反爬,我们这里还是用一下中间件,设置随机请求头

创建的是基础爬虫,不是通用爬虫,代码如下:

fang.py:开始爬虫与提取信息

# -*- coding: utf-8 -*-
import scrapy
import re
from fangtianxia.items import NewHouseItem,ESFHouseItemclass FangSpider(scrapy.Spider):name = 'fang'allowed_domains = ['fang.com']start_urls = ['https://www.fang.com/SoufunFamily.htm']def parse(self, response):#所有的城市链接都在td标签中#td标签包含在tr标签中#每一行的tr标签,从第3个开始是包含城市链接的td标签#获取所有的tr标签#过滤掉中国的港澳台,和国外房源[0:-2]trs = response.xpath("//div[@class='outCont']//tr")[0:-2]#遍历所有的tr标签for tr in trs:#找到所有的td表标签tds = tr.xpath(".//td[not(@class)]")#这里过滤掉了含有class属性的td标签#所以包含城市链接和名字的td标签是第二项city_td = tds[1]#找到所有的参数的链接的a标签city_links = city_td.xpath(".//a")#遍历所有的城市链接for city_link in city_links:#链接内的文本信息即城市名称city = city_link.xpath(".//text()").get()#href属性即链接city_url = city_link.xpath(".//@href").get()# print(city,city_url)#构造新房和二手房的链接#北京的url特殊处理if "bj." in city_url:#北京新房链接newhouse_url = "https://newhouse.fang.com/"#北京二手房链接esf_url = "https://esf.fang.com/"else:#用split方法以“.”分割url#如“http://cq.fang.com/”#分割成“http://cq”和“fang”和“com/”url_s = city_url.split(".")first = url_s[0]second = url_s[1]last = url_s[2]#新房链接newhouse_url = first + ".newhouse." + second + "." + last#二手房链接esf_url = first + ".esf." + second + "." + last# print(city)# print(newhouse_url)# print(esf_url)#把新房,二手房链接传给解析函数处理。提取信息,并把城市名传过去yield scrapy.Request(url=newhouse_url,callback=self.parse_newhouse,meta={"info":(city)})yield scrapy.Request(url=esf_url, callback=self.parse_esf,meta={"info": (city)})# break# breakdef parse_newhouse(self,response):city = response.meta.get('info')# print(city)#包含一页的div下的li标签包含房源信息#获取所有li标签,遍历处理lis = response.xpath("//div[@class='nhouse_list']//li")for li in lis:#判断该li标签是不是广告。#是广告的li标签含有h3标签,以此过滤is_gg = li.xpath(".//div[@class='clearfix']/h3")if is_gg:continueelse:#获取小区名字name = li.xpath(".//div[@class='nlcd_name']/a/text()").get()#使用正则表达式去除空白name = re.sub(r"\s","",name)# print(name)#获取居室rooms = "".join(li.xpath(".//div[contains(@class,'house_type')]//a/text()").getall())rooms = re.sub(r"\s", "", rooms)# print(rooms)#获取面积area = "".join(li.xpath(".//div[contains(@class,'house_type')]/text()").getall())area = re.sub(r"\s|/|-", "", area)# print(area)# 获取位置#为避免获取信息不全,直接获取title属性address = li.xpath(".//div[@class='address']/a/@title").get()address = re.sub(r"\s|\[|\]", "", address)# print(address)#待售或在售sale = li.xpath(".//div[contains(@class,'fangyuan')]/span/text()").get()# print(sale)#价格price = "".join(li.xpath(".//div[@class='nhouse_price']//text()").getall())price = re.sub(r"\s|广告", "", price)# print(price)#获取行政区#有些小区信息未正常给出行政区名称,导致没有span标签,在此过滤,不获取该小区信息is_span = li.xpath(".//div[@class='address']//span")if not is_span:continuedistrict = li.xpath(".//div[@class='address']//span[@class='sngrey']/text()").get()#[]是特殊符号,需要转义district = re.sub(r"\s|\[|\]", "", district)# print(district)item = NewHouseItem(city=city,name=name,rooms=rooms,area=area,address=address,price=price,sale=sale,district=district)# print(item)yield item#判断是否有下一页,有则继续获取next_page = response.xpath("//a[@class='next']/@href").get()# print(next_page)if next_page:yield scrapy.Request(url=response.urljoin(next_page),callback=self.parse_newhouse,meta={"info":(city)})def parse_esf(self,response):city = response.meta.get('info')# print(city)# 所有的信息列表存放在dl标签中dls = response.xpath("//dl[@class='clearfix']")for dl in dls:#判断是不是广告is_gg = dl.xpath(".//h3")if is_gg:continueelse:#获取小区名name = dl.xpath(".//p[@class='add_shop']/a/@title").get()# print(name)# 地址address = dl.xpath(".//p[@class='add_shop']//span/text()").get()# print(address)# 价格price = "".join(dl.xpath(".//dd[@class='price_right']//span[@class='red']//text()").getall())# print(price)# 独栋或居室,朝向,面积等详细信息info = "".join(dl.xpath(".//p[@class='tel_shop']//text()").getall())info = re.sub(r"\s","",info)# print(info)item = ESFHouseItem(city=city,name=name,address=address,price=price,info=info)# print(item)yield itemnext_page = response.xpath("//div[@class='page_al']/p/a/@href").get()if next_page:yield scrapy.Request(url=response.urljoin(next_page),callback=self.parse_esf,meta={"info":(city)})

注释和测试代码都很详细,供参考。

item.py:

import scrapyclass NewHouseItem(scrapy.Item):#城市city = scrapy.Field()#小区名字name = scrapy.Field()#居室rooms = scrapy.Field()#面积area = scrapy.Field()#行政区district = scrapy.Field()#位置address = scrapy.Field()#待售或在售sale = scrapy.Field()#价格price = scrapy.Field()class ESFHouseItem(scrapy.Item):#城市city = scrapy.Field()#小区name = scrapy.Field()#地址address = scrapy.Field()#价格price = scrapy.Field()#独栋或居室,朝向,面积,出售人等详细信息info = scrapy.Field()

middlewares.py:仅用于设置随机请求头,由于网站无反爬,可不写

import randomclass UserAgentDownloadMiddleware(object):User_Agents = ['Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0','Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.13; ko; rv:1.9.1b2) Gecko/2008''1201 Firefox/60.0','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'' Chrome/70.0.3538.77 Safari/537.36','Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML like Gecko) Chrome''/44.0.2403.155 Safari/537.36',]def process_request(self, request, spider):user_agent = random.choice(self.User_Agents)request.headers['User-Agent'] = user_agent

piplines.py:这里写了两个piplines,一个用于把新房信息存储到MySQL,一个用于把二手房存储mongo

import pymysql
import pymongo
from fangtianxia.items import NewHouseItem,ESFHouseItemclass NewhouseSQLPipeline(object):def __init__(self):self.db = pymysql.connect(host='localhost', port=3306, user='root',password='123',db='fangtianxia',charset='utf8')self.cursor = self.db.cursor()def process_item(self, item, spider):sql = """insert into newhouse(id,city,name,rooms,area,district,address,sale,price) values(null,%s,%s,%s,%s,%s,%s,%s,%s)"""#判断是否为新房的item,是就储存if isinstance(item, NewHouseItem):try:self.cursor.execute(sql,(item['city'],item['name'],item['rooms'],item['area'],item['district'],item['address'],item['sale'],item['price']))self.db.commit()print("SQL存储成功")except pymysql.Error as e:print("ERROR:", e.args)return itemclass EsfMONGOPipeline(object):def __init__(self,mongo_uri,mongo_db):self.mongo_uri = mongo_uriself.mongo_db = mongo_db@classmethoddef from_crawler(cls,crawler):return cls(mongo_uri=crawler.settings.get("MONGO_URI"),mongo_db=crawler.settings.get("MONGO_DB"))def open_spider(self,spider):self.client = pymongo.MongoClient(self.mongo_uri)self.db = self.client[self.mongo_db]def process_item(self, item, spider):name = item.__class__.__name__if isinstance(item, ESFHouseItem):try:self.db[name].insert(dict(item))print("MONGO存储成功")return itemexcept Exception as e:print("ERROR",e.args)def close_spider(self,spider):self.client.close()

settings.py:

关闭协议:

ROBOTSTXT_OBEY = False

打开延迟:

DOWNLOAD_DELAY = 3

默认请求头信息:

DEFAULT_REQUEST_HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language': 'en',
}

下载中间件:

DOWNLOADER_MIDDLEWARES = {'fangtianxia.middlewares.UserAgentDownloadMiddleware': 543,
}

piplines:

ITEM_PIPELINES = {'fangtianxia.pipelines.NewhouseSQLPipeline': 300,'fangtianxia.pipelines.EsfMONGOPipeline': 100,
}
MONGO_URI='localhost'
MONGO_DB='fangtianxia'

最后把start.py写一下就可以运行了。

这里需要说明一下,优先级高即数字小的piplines全部运行完,才会运行优先级低的piplines。

比如上面的,mongo储存完才会储存mysql

数据库:

MySQL:

MonGo:

后续把它变成分布式爬虫的项目最近就会写上来。

这篇关于房天下房源爬取:scrapy-redis分布式爬虫(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/278381

相关文章

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Python3 BeautifulSoup爬虫 POJ自动提交

POJ 提交代码采用Base64加密方式 import http.cookiejarimport loggingimport urllib.parseimport urllib.requestimport base64from bs4 import BeautifulSoupfrom submitcode import SubmitCodeclass SubmitPoj():de

集中式版本控制与分布式版本控制——Git 学习笔记01

什么是版本控制 如果你用 Microsoft Word 写过东西,那你八成会有这样的经历: 想删除一段文字,又怕将来这段文字有用,怎么办呢?有一个办法,先把当前文件“另存为”一个文件,然后继续改,改到某个程度,再“另存为”一个文件。就这样改着、存着……最后你的 Word 文档变成了这样: 过了几天,你想找回被删除的文字,但是已经记不清保存在哪个文件了,只能挨个去找。真麻烦,眼睛都花了。看

开源分布式数据库中间件

转自:https://www.csdn.net/article/2015-07-16/2825228 MyCat:开源分布式数据库中间件 为什么需要MyCat? 虽然云计算时代,传统数据库存在着先天性的弊端,但是NoSQL数据库又无法将其替代。如果传统数据易于扩展,可切分,就可以避免单机(单库)的性能缺陷。 MyCat的目标就是:低成本地将现有的单机数据库和应用平滑迁移到“云”端

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据,由于缓存中没有命中,会去数据库中查询,而数据库中也没有该数据,并且每次查询都不会命中缓存,从而每次请求都直接打到了数据库上,这会给数据库带来巨大压力。 二、布隆过滤器原理 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用多个不同的哈希函数将一个元素映射到一个位数组中的多个位置,并将这些位置的值置

Python:豆瓣电影商业数据分析-爬取全数据【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】

**爬取豆瓣电影信息,分析近年电影行业的发展情况** 本文是完整的数据分析展现,代码有完整版,包含豆瓣电影爬取的具体方式【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】   最近MBA在学习《商业数据分析》,大实训作业给了数据要进行数据分析,所以先拿豆瓣电影练练手,网络上爬取豆瓣电影TOP250较多,但对于豆瓣电影全数据的爬取教程很少,所以我自己做一版。 目

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

laravel框架实现redis分布式集群原理

在app/config/database.php中配置如下: 'redis' => array('cluster' => true,'default' => array('host' => '172.21.107.247','port' => 6379,),'redis1' => array('host' => '172.21.107.248','port' => 6379,),) 其中cl

Golang 网络爬虫框架gocolly/colly(五)

gcocolly+goquery可以非常好地抓取HTML页面中的数据,但碰到页面是由Javascript动态生成时,用goquery就显得捉襟见肘了。解决方法有很多种: 一,最笨拙但有效的方法是字符串处理,go语言string底层对应字节数组,复制任何长度的字符串的开销都很低廉,搜索性能比较高; 二,利用正则表达式,要提取的数据往往有明显的特征,所以正则表达式写起来比较简单,不必非常严谨; 三,使

Golang网络爬虫框架gocolly/colly(四)

爬虫靠演技,表演得越像浏览器,抓取数据越容易,这是我多年爬虫经验的感悟。回顾下个人的爬虫经历,共分三个阶段:第一阶段,09年左右开始接触爬虫,那时由于项目需要,要访问各大国际社交网站,Facebook,myspace,filcker,youtube等等,国际上叫得上名字的社交网站都爬过,大部分网站提供restful api,有些功能没有api,就只能用http抓包工具分析协议,自己爬;国内的优酷、