热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

轩宝:python小白的第一只爬虫“房天下”在售新房数据爬取

仅以此文纪念我的第一只虫子!先上干货,由于是初学者,代码略渣,欢迎交流学习。整个虫主要由四个模块组成:URL管理器(url_manager)、下载器(html_downloader

仅以此文纪念我的第一只虫子!

先上干货,由于是初学者,代码略渣,欢迎交流学习。

整个虫主要由四个模块组成:URL管理器(url_manager)、下载器(html_downloader)、解析器(html_parser)和输出器(html_outputer)。

没错,整个爬虫代码主要是参照我的启蒙爬虫教学视频,慕课网“Python开发简单爬虫”中的百度百科1000个页面的爬取视频所得Python开发简单爬虫视频教程,所以可以发现借鉴痕迹较重,在此基础上,根据具体页面内容和需要对其进行了部分改造。个人觉得这个视频内容还是一个不错的入门级爬虫教育视频,介绍了爬虫的工作原理,还配合具体案例实现,有兴趣的朋友可以看看。

程序的思想很简单,就是将房天下上的主要城市的在售楼盘数据爬下来:通过CityUrl_Crwa()获取每一个城市的在售楼盘列表起始页,将获取到的城市楼盘起始页链接循环放入crwa()中,分别获取每一个城市的楼盘数据。在crwa()中,通过URL管理器(url_manager.py)对列表页链接进行管理,然后对每一个列表页页面下载(html_downloader.py)后,进行解析(html_parser.py),解析的过程中爬取了楼盘的名称、详情页链接、地址、价格四个字段,并获取了页面最下方分页链接,该链接是爬虫不断爬取数据的url来源。最后输出(html_outputer.py)到excel(output_excel())中或mysql(output_mysql())数据库中。

下面开始代码部分:

首先是主程序spider_main.py:

# coding=utf-8
__author__ = 'zyx'
import url_manager,html_downloader,html_parser,html_outputer
class SpiderMain(object):
def __init__(self):
self.urls=url_manager.UrlManager()#管理URL
self.downloader=html_downloader.HtmlDownloader()#下载URL内容
self.parser=html_parser.HtmlParser()#解析URL内容
self.outputer=html_outputer.HtmlOutputer()#输出获取到的内容
#获取待爬取城市链接
def CityUrl_Crwa(self,root_url):
html_cOnt=self.downloader.download(root_url)
cityurls_list=self.parser.cityurlparser(html_cont)
return cityurls_list
#爬虫主体程序
def crwa(self,city_url):
count=0
self.urls.add_new_url(city_url)#将根链接首先放入page页urllist
while self.urls.has_new_url():
count=count+1
try:
new_url=self.urls.get_new_url()#获取新的链接
html_cOnt=self.downloader.download(new_url)#下载页面内容
new_urls, new_data ,house_city= self.parser.parse(new_url, html_cont)#解析页面内容
self.urls.add_new_urls(new_urls)
#self.outputer.output_excel(new_data,house_city)#写入excel
self.outputer.output_mysql(new_data,house_city)#写入mysql
print "第",count,"个网页【",new_url,"】输出成功--------------"
except:
self.urls.add_false_url(new_url)#如果解析失败,则将url放入失败列表
print "第",count,"个网页【",new_url,"】爬取失败,将会被重新爬取,失败次数过多将会被舍弃-------------"
count=count-1
self.urls.release_urllist()#解析并输出完一个城市后,释放page页urllist
if __name__=="__main__":
obj_spider=SpiderMain()
root_url="http://newhouse.wuhan.fang.com/house/s/b81-b91/"
city_urls=obj_spider.CityUrl_Crwa(root_url)
for city_url in city_urls:
obj_spider.crwa(city_url)

最开始尝试爬取的是一个城市的数据,成功后就想着爬取所有城市数据。这时候,第一个方案想的是将所有城市的列表页链接从网上人工弄下来,人工构建好以后,放入city_url.txt文档,用程序读txt里面的城市链接,并进行爬取。虽然这个也能够完成任务,但是为了体现出爬虫的自主性,于是第二个方案是用程序自动爬取城市url,爬完城市url后,再对每一个城市的列表也进行爬取。于是产生了CityUrl_Crwa()和crwa()。

url管理器:url_manager.py

#coding=utf8
__author__ = 'zyx'
class UrlManager(object):
def __init__(self):
self.new_urls=set()
self.old_urls=set()
self.false_urls=[]
def add_new_url(self, url):
if url is None:
return
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url)
def add_new_urls(self, urls):
if urls is None or len(urls)==0:
return
for url in urls:
self.add_new_url(url)
def has_new_url(self):
return len(self.new_urls)!=0
def get_new_url(self):
new_url=self.new_urls.pop()
self.old_urls.add(new_url)
return new_url
#将解析失败的URL重新放入url列表进行解析
def add_false_url(self,url):
#如果一个URL被反复循环爬取多次失败,就放弃该url
if url is None or self.false_urls.count(url)>4:
return
self.false_urls.append(url)
self.old_urls.remove(url)
self.add_new_url(url)
#每爬取完一个城市,清空old_url和false_urls列表
def release_urllist(self):
if len(self.new_urls)==0:
self.old_urls.clear()
self.false_urls=[]

在Python开发简单爬虫视频教程该教程里面没有后两个方法add_false_url()和release_urllist(),设计这个两个方法的目的是因为在测试的过程中,发现有些页面列表页可能会存在一次解析不成功的行为,特别是网速 不太好的时候。因此增加了一个解析失败的方法add_false_url,可以对这种列表页进行反复解析,测试的过程中发现一般最多解析3次就会爬取得到该页面数据。release_urllist()的设计是为了爬取完一个城市的页面,就释放已有的列表队列。

下载器:html_downloader.py

# coding=utf-8
__author__ = 'zyx'
import requests
class HtmlDownloader(object):
def download(self, url):
if url is None:
return None
html=requests.get(url)
html=html.text.encode(html.encoding).decode("gbk").encode("utf8")
return html

该功能较为简单,就是使用requests获取页面内容。需要注意的是爬虫中的会普遍遇到的坑,即页面编码问题,反正我是在此摔了大跟头,因为涉及到后面存储到数据库中,页面编码、数据库编码问题折腾了我好几天,一度难以推进,好在最后还是解决了。

解析器:html_parser.py

#coding=utf8
__author__ = 'zyx'
import urlparse
from bs4 import BeautifulSoup
import re
class HtmlParser(object):
def cityurlparser(self,html_cont):
soup=BeautifulSoup(html_cont,'html.parser',from_encoding='utf8')
city_urls=set()
linklist=soup.find('div',class_="city20141104").find_all('a',href=True)
[city_urls.add(city_url["href"]+"b81-b91/") for city_url in linklist]
return city_urls
def parse(self,page_url,html_cont):
if page_url is None or html_cont is None:
return
soup=BeautifulSoup(html_cont,'html.parser',from_encoding='utf8')
new_urls=self._get_new_urls(page_url,soup)
new_data=self._get_new_data(soup)
city=self._get_city(soup)
return new_urls,new_data,city
#获取分页链接
def _get_new_urls(self,page_url,soup):
new_urls=set()
links=soup.find('div',class_="page").find_all('a',href=re.compile(r"/house/s/b81-\w+"))
for link in links:
new_url=link['href']
new_full_url=urlparse.urljoin(page_url,new_url)
new_urls.add(new_full_url)
return new_urls
#获取页面内容
def _get_new_data(self,soup):
res_data=[]
nodes=soup.find_all('div',class_="nlc_details")
for node in nodes:
house_data={}
house_name=node.find('div',class_="nlcd_name").get_text()
house_data['house_name']=''.join(house_name.split())
house_tag=node.find('div',class_="nlcd_name").find('a',href=True).get("href")
house_data['house_tag']=''.join(house_tag.split())
try:
house_address=node.find('div',class_="address").get_text()
house_data['house_address']=''.join(house_address.split())
except:
house_data['house_address']=""
try:
house_price=node.find('div',class_="nhouse_price").get_text()
house_data['house_price']=''.join(house_price.split())
except:
house_data['house_price']=""
res_data.append(house_data)
return res_data
def _get_city(self,soup):
house_city=soup.find('div',class_="s4Box").get_text()
return house_city

解析部分主要使用了BeautifulSoup解析页面内容,urlparse组合新的城市分页链接,re匹配href。_get_new_urls()获取分页链接,放入new_url队列中,供爬虫爬取相关页面使用,这也是这个爬虫程序不断爬取新的页面url来源。_get_new_data()获取列表中每一个楼盘的数据。由于价格和地址两个字段,有些楼盘有缺失,于是做了简单的处理。_get_city()获取当前爬取的城市字段。

最后是输出器:html_outputer.py

#coding=utf8
__author__ = 'zyx'
import MySQLdb
import xlrd
from xlutils.copy import copy
import time
class HtmlOutputer(object):
#写入EXCEL
def output_excel(self,new_data,house_city):
file=xlrd.open_workbook("soufang.xls",formatting_info=True)
#获取已有sheet的行数
nrow=file.sheets()[0].nrows
if nrow!=0:
nrow==nrow+1
#复制原有sheet
copy_file=copy(file)
sheet=copy_file.get_sheet(0)
#插入数据
for row,item in enumerate(new_data):
sheet.write((row+nrow),4,house_city)
for i,value in enumerate(item.values()):
sheet.write((row+nrow),i,value)
copy_file.save('soufang.xls')
#写入数据库
def output_mysql(self,new_data,house_city):
db=MySQLdb.connect(host="localhost",user="root",passwd="root",db="test",charset="utf8")
cursor=db.cursor()
try:
for item in new_data:
values=item.values()
sql=("insert into SouFang (HouseAddr,HouseTag,HouseName,HousePrice,HouseCity,CrawDate) values('%s','%s','%s','%s','%s','%s')"
%(values[0].encode("utf8"),values[1].encode("utf8"),
values[2].encode("utf8"),values[3].encode("utf8"),
house_city.encode("utf8"),time.strftime('%Y-%m-%d',time.localtime(time.time())).encode("utf8"))
)
cursor.execute(sql)
db.commit()
except:
print item["house_name"],"false"
db.rollback()#发生错误时回滚
db.close()

输出方式设计了excel和mysql两种数据存储格式。存储到数据库中需要注意格式编码的问题。本次采集共获得2W+条数据:

—————以下为学习python的过程杂记,不感兴趣的小伙伴可自行忽略—————————

校招进了某国企,8月只身来到杭州闯荡。没有任何亲人朋友,有的只是彼此陌生的小伙伴。国企二次分配,去了杭州某县城,巨大的落差感觉人生跌倒了最低谷。不感兴趣的工作内容,小县城慢节奏的无聊生活,以及孤身闯荡的形单影只,把刚参加工作的兴奋蹂躏的粉碎。在听说有个早几个月来的前辈,没干够一周就辞职的光辉事迹后,内心不甘的小人儿冲破了枷锁,在女友和家人的支持下开始骑驴找马,一边学习python,一边寻找自己喜欢的工作。就这样开始走上了python的不归路。

python的学习首要的问题是不知道如何下手。说来惭愧,由于大学期间就系统的学过C,连JAVA都没学过,所以基础并不怎么好。但是自己却在一些项目中多少接触或自学过php,jquery,css,java等,但是却都是由于缺乏具体的项目实践,学的半吊子。python学习的过程中,最开始是想着跟着视频学,为此在网上找了一堆视频,但是发现看视频太慢了。于是放弃了,转战书籍资料,开始跟着书本学习python语法。有幸我们身在一个互联网的时代,信息获取廉价又方便,知乎中的知友提供了很多帮助,很多关于python学习的问答给予了方向。这里关于python基础语法的学习资料推荐慕课网的Python入门_python入门视频教程和python进阶视频教程,以及该网站上python其他的视频,都讲的还不错,至少对我受益匪浅。另外就是Python教程 – 廖雪峰的官方网站廖老师的这个网站上的python资料也是很不错,在基础知识上通熟易懂,受益良多。

与大多数程序语言的学习过程相类似,学习了基础语法其实还是不会写程序。当我学完Python以后,我仍然是看不懂别人写的代码,当然自己也不懂如何写大块大块的代码。(>﹏<)。于是乎陷入了纠结中。于是乎疯狂在知乎上各种看别人的学习心得、学习路径。其中有位大神的回答让我茅塞顿开:“不要问怎么入门,直接上路就好了”,说的好像很有道理。于是乎就参照指数上各位大神推荐的学习路径,找到了一份python小程序的学习案例GitHub – Yixiaohan/show-me-the-code: Python 练习册,每天一个小程序,号称100个,实际上我没发现100个。管他有没有100个,直接上路开搞。就这样,对着这些小程序就开始了python的编程学习之路。学习的过程是痛苦的,拿着一个小案例,想了半天也不知道该如何下手,一脸懵逼。后来就想想,既然不会写,那就默写好了,先把对方的答案弄出来看一遍,看两遍….看懂记住为止,然后再pycharm中就默写了起来,默写一遍不对,再看再默写,直到能够实现。在看的过程中就需要去拆解没一行程序,为什么是这样写,为什么可以这样写,不懂的就去查相应的方法函数,一遍一遍又一遍。慢慢的默写了几个小程序之后,就有了点感觉,再然后就能自己写几句,不断的查阅资料去实现一个小的功能,或在别人已完成的基础上改变一些实现语句、方法。在这过程中学习了对文件的操作,对数据库,对excel的操作等,写了一个实现tfidf的渣渣代码等等,后来才知道有相关的tfidf包可以用。。。。。

再后来就想到挑战一下python爬虫,因为我的目的是要学习数据分析,数据分析的前提是数据,因此在有一定感觉和写作代码的基础上又开始了爬虫的学习。最开始想着的是对QQ空间数据的爬取,分析一下自己好友的空间说说数据,但是后来发现需要模拟登陆,很是麻烦,而且还是动态页面。于是只好先妥协,学学静态页面的数据抓取。于是乎跟着Python开发简单爬虫视频教程上面相关的一些课程慢慢学习爬虫的知识。最开始是跟着视频里面的教学内容,实现了对百度百科内容的获取,但是后来一想不甘心就这样跟着别人做,想着自己写一个静态的爬虫。以什么为对象呢,刚好各地方出了房产的一些政策,那就爬房产数据好了。于是乎白天上班,晚上加班学习写爬虫,但是由于自身能力有限,经过大半个月,才逐渐成型,且经过多次修改和改善相关功能,才成为现在的这只虫虫(^-^)V。程序中也还有诸多问题没解决,比如突然断网了,就TM得重新爬/(ㄒoㄒ)/~~,还有如何实现增量爬取也没搞定。。。。

接下来想继续学习一些爬虫框架,学习抓取动态页面数据,学习模拟登陆等等,同时学习数据分析、机器学习、数据可视化相关包。

任重道远,且行且努力吧O(∩_∩)O~~


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
author-avatar
我的生活我做主哦耶_266
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有