分享
 
 
 

巧用web机器人搜索廉价二手房

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

1. 问题

我需要一套二室一厅,价格在40万左右的,位于上海某几个区(例如徐汇,闵行区)的二手房。我理想中的房子当然需要满足很多条件,例如它必须是两室都朝南的房子,它的均价必须低于7000元每平方米,等等。

通常我使用上海热线的二手房网(http://secondhand.online.sh.cn/)来查找最新的二手房信息。

但是每天跟踪网站的最新二手房信息太累人了。二手房网每天都要添加很多最新信息,为了识别出符合我的条件的房子,我必须把所有这些信息都看一遍。整个过程非常浪费时间。

原因在于:

1. 网站只会在首页显示最新加入的一些二手房的*概要*信息。而我为了作出自己的判断,光是看概要信息是不够的,我必须看详细信息。而这就意味着我必须点击概要信息中的名为“详细”的超链接来下载详细信息的页面。

2. 并不是所有信息都是放在一个页面上的。事实上,上海热线的二手房网的信息是分页显示的,每个页面大概只显示10条信息。如果我需要看所有的信息的话,我需要看大概500-3000条信息左右(信息的多少和搜索条件是否严格,搜索的区域的大小有关系)。

上述1,2点的意思就是,即使我只需要看500条信息,我也需要看550个页面:

500/10(概要信息页面)+500*1(每条信息的详细页面)=550

每个页面载入的时间是10秒(实际取样的结果),我阅读每个页面的时间是25秒(我的阅读速度是非常快的了)。

550*(10+25)=19250秒=320分钟=5小时。

也就是说,光是阅读信息我就需要花5小时的时间。事实上,我还没有考虑对信息进行分析(例如列出均价最低的10处房产)的时间呢,这远比阅读更花时间。

2. 问题的本质

由于现在很多信息都放在网上了,类似的问题很常见。例如招聘,证券等等,都有同样的问题。这个问题就是,如何高效率地从internet上获得数据并进行分析。

3.一个方案

我看了一些国内的相关文章,这些文章的方案都是基于IE的自动化的。通常使用VB语言加上WebBrowser控件。

这种方案最主要的缺点是由于WebBrowser对很多数据操作进行了封装,使得本来很简单的操作变得麻烦无比(我猜原因是因为微软设计WebBrowser的目的是自动化IE的操作)。例如取得当前页面这样一个操作,如果使用WebBrowser开发的话竟然需要分取得当前页面,在某个事件中获得取得页面成功两个进程。为了使这两个进程通信又需要很多额外的工作。

我的方案是使用python开发一个脚本,因为python的库支持比较完备,这样还可以获得跨平台的额外好处。我使用结构化的设计,因为使用web机器人取得数据这样的事情本身就是结构化的。我将和具体网站相关的操作都封装在某几个函数内,这意味着该脚本如果经过适当的改写,还能有其他应用。也就是,这个脚本不仅仅是用于上海热线二手房网的,也不仅仅是先于搜索二手房的。

在写这个脚本之前,我已经有一些边写web机器人的经验,使用的语言包括VB,Java,Python等等。我认为目前我写的这个web机器人是比较成熟的。

这个方案的要点是这样的,首先向二手房网提交条件,然后获得分页搜索结果,从第一页中读取各信息条款(),读取共有多少页面等等必要的信息(这些信息的读取同样需要硬编码起始标志和结束标志)等等。然后根据这些信息模拟翻页,点击“详细”链接等动作。这些动作无非就是http标准中的get,post动作而已。

上述方案中的某些地方需要硬编码,必要的话需要分析浏览器客户端脚本(例如JavaScript):

*各信息条款的起始标志和结束标志

*当前页面计数的起始标志和结束标志

*页面总数的起始标志和结束标志

*翻页动作中的超链接地址

*点击“详细”超链接中的超链接地址

获得的数据我都打印在屏幕上,这些数据使用逗号分割,如果希望把数据导入到文件中,可以使用管道机制。

例如,假设我写的脚本是house.py,你希望把house.py的数据导入到文件house.csv中去,你可以输入这样的命令:

python house.py > house.csv

接下来你可以就可以分析house.csv了。我通常用支持正则表达式的编辑器去掉house.csv中的一些格式化字符,再用excel进行数据分析。

4. 具体实现以及代码

具体实现中还碰到一些小问题。例如为了支持中文,我必须再脚本的开头加上“# -*- coding: mbcs -*-”这一行。我上网需要使用指定的代理服务器,所以我使用urllib2库而不是urllib库。这些问题都已经在源代码中说明了。

下面就是源代码:

# -*- coding: mbcs -*-

#------------------user configuration------------begin

#ÊÇ·ñʹÓÃÐèÒªÑéÖ¤µÄhttp´úÀí£¿£¨½öÏÞÓÚ¼«ÉÙÊý¹«Ë¾,´ó¶àÊýÇé¿öÏÂÖÃΪ0)

config_using_proxy=0

#´úÀíµØÖ·ºÍ¶Ë¿ÚºÅ£¬½öµ±config_using_proxyµÈÓÚ1ʱÓÐЧ

config_httpproxy=

#µÇ½´úÀí·þÎñÆ÷µÄÓû§Ãû£¬½öµ±config_using_proxyµÈÓÚ1ʱÓÐЧ£¬·ñÔò¿ÉÒÔºöÂÔ

config_username=

#µÇ½´úÀí·þÎñÆ÷µÄÃÜÂ룬½öµ±config_using_proxyµÈÓÚ1ʱÓÐЧ£¬·ñÔò¿ÉÒÔºöÂÔ

config_password=

#Ñ¡ÔñËùÔÚÇøÏØ£¨"","ãÉÐÐ","Ðì»ã","ÑîÆÖ","ºç¿Ú","³¤Äþ","¾²°²","¬Íå","»ÆÆÖ","Õ¢±±","ÆÕÍÓ","ÆÖ¶«","±¦É½","¼Î¶¨","ÇàÆÖ","·îÏÍ","ÄÏ»ã","½ðɽ","Ëɽ­","³çÃ÷"),""±íʾËùÓÐ

config_szqx="ºç¿Ú"

#Ñ¡Ôñ¼¸ÊÒ£¿ £¨"", "1","2","3","4","5")

config_fx_room="2"

#Ñ¡Ôñ¼¸Ìü£¿ £¨"","0","1","2","3","4")

config_fx_hall="1"

#Ñ¡Ôñ×ܼÛÏÂÏÞ£¨µ¥Î»£ºÍò),£¨"0","10","15","20","25","30","35","40","50","60","70","80","90","100"),"0"±íʾ²»ÏÞ

config_jg_min="20"

#Ñ¡Ôñ×ܼÛÉÏÏÞ£¨µ¥Î»£ºÍò),£¨"10000000","10","15","20","25","30","35","40","50","60","70","80","90","100"),"10000000"±íʾ²»ÏÞ

config_jg_max="60"

#Ñ¡Ôñ½»Ò×·½Ê½, ("0","1"), 0±íʾ³öÊÛ,1±íʾÖû»

config_type="0"

#µÇ¼ÇÈÕÆÚ(µ¥Î»,Ìì),("15","30","180",""),""±íʾ²»ÏÞ

config_djrq="15"

#ËÑË÷±í¸ñµÚÒ»ÐпªÊ¼µÄ±êÖ¾×Ö·û´®£¨µÚÒ»ÐдӸÃ×Ö·û´®Ö®ºó¿ªÊ¼ËÑË÷ÐпªÊ¼±êÖ¾)

config_tbl_begin_str=">ÇøÏØ<"

#ËÑË÷±í¸ñ×îºóÒ»ÐÐĩβµÄ±êÖ¾×Ö·û´®£¨×îºóÒ»ÐдӸÃ×Ö·û´®Ö®Ç°¿ªÊ¼·´ÏòËÑË÷ÐнáÊø±êÖ¾)

config_tbl_end_str="¹²ÕÒµ½·ûºÏÌõ¼þµÄ³öÊÛ·¿Ô´ÐÅÏ¢"

#------------------------user configuration---------------end

#------------------------administrator configuration------begin

config_post_data={"szqx":config_szqx,"fx_room":config_fx_room,"fx_hall":config_fx_hall,"jg_min":config_jg_min,"jg_max":config_jg_max,"type":config_type,"djrq":config_djrq,"sortfield":"djrq","sorttype":"desc","whichpage":"1"}

#------------------------administrator configuration------end

from string import *

import sys

#-----------------print routines-------------begin

def dump_row_end():

sys.stdout.write('\n')

def dump_table_begin():

sys.stdout.write("ÇøÏØ"+",")

sys.stdout.write("ÎïÒµµØÖ·"+",")

sys.stdout.write("·¿ÐÍ"+",")

sys.stdout.write("ÎïÒµÀàÐÍ"+",")

sys.stdout.write("½¨ÖþÃæ»ý"+",")

sys.stdout.write("×ܼÛ"+",")

sys.stdout.write("µÇ¼Çʱ¼ä"+",")

sys.stdout.write("·¿Ô´±àºÅ"+",")

sys.stdout.write("ÎïÒµÃû³Æ"+",")

sys.stdout.write("·¿¡¡¡¡Áä"+",")

sys.stdout.write("²úȨ˵Ã÷"+",")

sys.stdout.write("Öн鱨¼Û"+",")

sys.stdout.write("ÎïÒµÏêϸµØÖ·"+",")

sys.stdout.write("·¿Îݳ¯Ïò"+",")

sys.stdout.write("ËùÔÚÂ¥²ã"+",")

sys.stdout.write("×°Ð޳̶È"+",")

sys.stdout.write("ÊÒÄÚÌõ¼þ"+",")

sys.stdout.write("ÓÐЧÆÚÏÞ"+",")

sys.stdout.write("±¸¡¡¡¡×¢"+",")

sys.stdout.write("Áª ϵ ÈË"+",")

sys.stdout.write("ÁªÏµµç»°"+",")

sys.stdout.write("\n")

def dump_one_field(str):

sys.stdout.write(str+",")

#-----------------print routines-------------end

#-----------------house parser-------------begin

def get_last_page_number(s):

no_begin=find(s,"βҳ")

if no_begin==-1:

return 0

no_begin=rfind(s,"javascript:form_submit(\'",0,no_begin)

no_begin+=len("javascript:form_submit(\'")

no_end=find(s,"\'",no_begin)

if no_end==-1:

return 0

if no_begin>no_end:

return 0

return atoi(s[no_begin:no_end])

def get_data_in_one_tag(instr4,tag):

tag_begin=find(instr4,"<"+tag)

if tag_begin==-1:

return instr4

tag_begin=find(instr4,">")

if tag_begin==-1:

return instr4

tag_begin+=1

tag_end=find(instr4,"</"+tag+">",tag_begin)

if tag_end==-1:

return instr4

return instr4[tag_begin:tag_end]

def filter_rubbish_data(str):

return strip(replace(replace(replace(replace(str,","," "),'\n',''),'\t',''),'\r','')) #maybe we will output data in csv format

def get_one_detailed_data(instr3,keyword):

#print instr3 #debug

#print keyword #debug

data_begin=find(instr3,keyword)

if data_begin==-1:

return ""

# handle data

data_begin=find(instr3,"<td",data_begin)

if data_begin==-1:

return ""

data_begin=find(instr3,">",data_begin)

if data_begin==-1:

return ""

data_begin=data_begin+1

data_end=find(instr3,"</td>",data_begin)

if data_end==-1:

return ""

if data_begin>data_end:

return ""

# delete space , comma ,tab and linefeed

#return replace(instr3[data_begin:data_end],","," ")

return filter_rubbish_data(instr3[data_begin:data_end])

def get_detailed_data(instr2):

dump_one_field(get_one_detailed_data(instr2,"·¿Ô´±àºÅ"))

dump_one_field(get_one_detailed_data(instr2,"ÎïÒµÃû³Æ"))

dump_one_field(get_one_detailed_data(instr2,"·¿¡¡¡¡Áä"))

dump_one_field(get_one_detailed_data(instr2,"²úȨ˵Ã÷"))

dump_one_field(get_one_detailed_data(instr2,"Öн鱨¼Û"))

#delete the href

tmpstr=get_one_detailed_data(instr2,"ÎïÒµµØÖ·")

tmppos=find(tmpstr,"<a")

if tmpstr<>-1:

tmpstr=strip(tmpstr[:tmppos])

dump_one_field(tmpstr)

dump_one_field(get_one_detailed_data(instr2,"·¿Îݳ¯Ïò"))

dump_one_field(get_one_detailed_data(instr2,"ËùÔÚÂ¥²ã"))

dump_one_field(get_one_detailed_data(instr2,"×°Ð޳̶È"))

dump_one_field(get_one_detailed_data(instr2,"ÊÒÄÚÌõ¼þ"))

dump_one_field(get_one_detailed_data(instr2,"ÓÐЧÆÚÏÞ"))

dump_one_field(get_one_detailed_data(instr2,"±¸¡¡¡¡×¢"))

dump_one_field(get_data_in_one_tag(get_one_detailed_data(instr2,"Áª ϵ ÈË"),"div"))

dump_one_field(get_data_in_one_tag(get_one_detailed_data(instr2,"ÁªÏµµç»°"),"div"))

def get_data(instr,tbl_begin_str,tbl_end_str):

#table begin

idx=find(instr,tbl_begin_str)

if idx==-1:

return

idx=find(instr,"<tr",idx)

if idx==-1:

return

table_begin=idx

#print instr[table_begin:table_begin+100] #debug

#table end

idx=find(instr,tbl_end_str,table_begin)

#print instr[table_begin:idx]

if idx==-1:

return

idx=rfind(instr,"</tr>",table_begin,idx)

if idx==-1:

return

table_end=idx+len("</tr>")

#print instr[table_begin:table_end] #debug

#search rows

tr_idx=table_begin

while tr_idx<table_end:

#tr begin

tr_idx=find(instr,"<tr",tr_idx)

if tr_idx==-1:

return

tr_idx=find(instr,">",tr_idx)

if tr_idx==-1:

return

tr_begin=tr_idx+1

#tr end

tr_idx=find(instr,"</tr>",tr_begin)

if tr_idx==-1:

return

tr_end=tr_idx

#print instr[tr_begin:tr_end] #debug

#search cells in one row

td_idx=tr_begin

is_really_a_row_dumped=0

while td_idx<tr_end:

# td data begin

td_idx=find(instr,"<td",td_idx)

#print td_idx #debug

if td_idx==-1:

return

td_idx=find(instr,">",td_idx)

#print td_idx #debug

if td_idx==-1:

return

tddata_begin=td_idx+1

# td data end

td_idx=find(instr,"</td>",td_idx)

#print td_idx #debug

if td_idx==-1:

return

tddata_end=td_idx

if tddata_begin>tddata_end:

continue

if tddata_end>tr_end:

continue

if tddata_end>table_end:

continue

tddata=filter_rubbish_data(instr[tddata_begin:tddata_end])

#if the tddata is a href, let's get more data from the href

href_begin=find(tddata,"href=\"javascript:urll(\'")

if href_begin==-1:

dump_one_field(tddata)

continue

href_begin=href_begin+len("href=\"javascript:urll(\'")

href_end=find(tddata,"\'",href_begin)

if href_end==-1:

return

view_url="http://secondhand.online.sh.cn/"+tddata[href_begin:href_end]

#print view_url #debug

#dump_one_field(view_url)

view_result=urllib2.urlopen(view_url)

view_data=view_result.read()

#print "view_data="+view_data #debug

get_detailed_data(view_data)

is_really_a_row_dumped=1

if is_really_a_row_dumped: #sometimes, no td output

dump_row_end()

#-----------------house parser-------------end

def install_proxy():

httpproxy=config_httpproxy

username=config_username

password=config_password

httpproxystring='http://' + username + ':' + password + '@' + httpproxy

# build a new opener that uses a proxy requiring authorization

proxy_support=urllib2.ProxyHandler({"http":httpproxystring})

authinfo=urllib2.HTTPBasicAuthHandler()

opener = urllib2.build_opener(proxy_support, authinfo,urllib2.HTTPHandler)

# install it

urllib2.install_opener(opener)

#----------------main---------------------begin

if __name__=="__main__":

#get the page

import urllib2

import urllib

#using proxy

if config_using_proxy:

install_proxy()

f = urllib2.urlopen("http://secondhand.online.sh.cn/selllist.php",urllib.urlencode(config_post_data))

#print f.headers #debug

s=f.read()

#print s #debug

#parse the html page

#s="<table><tr><td>data11</td><td>data12</td></tr><tr><td>data21</td><td>data22</td></tr></table>" #debug

#config_tbl_begin_str="<table>" #debug

#config_tbl_end_str="</table>" #debug

# print out the table header

dump_table_begin()

# print out the first page

get_data(s,config_tbl_begin_str,config_tbl_end_str)

# get the page size from the first page data

last_page=get_last_page_number(s)

# print out other pages (if exist)

for i in range(2,last_page):

config_post_data['whichpage']=str(i)

f = urllib2.urlopen("http://secondhand.online.sh.cn/selllist.php",urllib.urlencode(config_post_data))

s=f.read()

get_data(s,config_tbl_begin_str,config_tbl_end_str)

#s="<td height=26>header1</td><td height=26>data1</td>" #debug

#print get_one_detailed_data(s,"header1") #debug

#print get_last_page_number("<a href=\"javascript:form_submit(\'51\',\'djrq\',\'desc\')\">βҳ</a>") #debug

#----------------main---------------------end

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有