热门标签 | 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~~


推荐阅读
  • 本文详细介绍了Socket在Linux内核中的实现机制,包括基本的Socket结构、协议操作集以及不同协议下的具体实现。通过这些内容,读者可以更好地理解Socket的工作原理。 ... [详细]
  • 我在尝试将组合框转换为具有自动完成功能时遇到了一个问题,即页面上的列表框也被转换成了自动完成下拉框,而不是保持原有的多选列表框形式。 ... [详细]
  • 本文详细介绍了PHP中的几种超全局变量,包括$GLOBAL、$_SERVER、$_POST、$_GET等,并探讨了AJAX的工作原理及其优缺点。通过具体示例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 本文详细介绍了在PHP中如何获取和处理HTTP头部信息,包括通过cURL获取请求头信息、使用header函数发送响应头以及获取客户端HTTP头部的方法。同时,还探讨了PHP中$_SERVER变量的使用,以获取客户端和服务器的相关信息。 ... [详细]
  • This article explores the process of integrating Promises into Ext Ajax calls for a more functional programming approach, along with detailed steps on testing these asynchronous operations. ... [详细]
  • 使用jQuery与百度地图API实现地址转经纬度功能
    本文详细介绍了如何利用jQuery和百度地图API将地址转换为经纬度,包括申请API密钥、页面构建及核心代码实现。 ... [详细]
  • 使用 ModelAttribute 实现页面数据自动填充
    本文介绍了如何利用 Spring MVC 中的 ModelAttribute 注解,在页面跳转后自动填充表单数据。主要探讨了两种实现方法及其背后的原理。 ... [详细]
  • 2023年1月28日网络安全热点
    涵盖最新的网络安全动态,包括OpenSSH和WordPress的安全更新、VirtualBox提权漏洞、以及谷歌推出的新证书验证机制等内容。 ... [详细]
  • selenium通过JS语法操作页面元素
    做过web测试的小伙伴们都知道,web元素现在很多是JS写的,那么既然是JS写的,可以通过JS语言去操作页面,来帮助我们操作一些selenium不能覆盖的功能。问题来了我们能否通过 ... [详细]
  • 本文旨在探讨Swift中的Closure与Objective-C中的Block之间的区别与联系,通过定义、使用方式以及外部变量捕获等方面的比较,帮助开发者更好地理解这两种机制的特点及应用场景。 ... [详细]
  • Java连接MySQL数据库的方法及测试示例
    本文详细介绍了如何安装MySQL数据库,并通过Java编程语言实现与MySQL数据库的连接,包括环境搭建、数据库创建以及简单的查询操作。 ... [详细]
  • 本文探讨了在AspNetForums平台中实施基于角色的权限控制系统的方法,旨在为不同级别的用户提供合适的访问权限,确保系统的安全性和可用性。 ... [详细]
  • Java中提取字符串的最后一部分
    本文介绍了如何使用Java中的substring()和split()方法来提取字符串的最后一部分,特别是在处理包含特殊字符的路径时的方法与技巧。 ... [详细]
  • 如何寻找程序员的兼职机会
    随着远程工作的兴起,越来越多的程序员开始寻找灵活的兼职工作机会。本文将介绍几个适合程序员、设计师、翻译等专业人士的在线平台,帮助他们找到合适的兼职项目。 ... [详细]
  • 本文介绍了用户界面(User Interface, UI)的基本概念,以及在iOS应用程序中UIView及其子类的重要性和使用方式。文章详细探讨了UIView如何作为用户交互的核心组件,以及它与其他UI控件和业务逻辑的关系。 ... [详细]
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社区 版权所有