python高级应用程序课程设计_Python高级应用程序设计任务

一、主题式网络爬虫设计方案(15分)

1.主题式网络爬虫名称

58同城房产抓取房产买卖信息

2.主题式网络爬虫爬取的内容与数据特征分析

本次爬虫主要爬取房产出售的位置、总价、面积、厅室等相关信息。

3.主题式网络爬虫设计方案概述(包括实现思路与技术难点)

思路:通过requests保持浏览器的登录状态,beautifulsoup对网页中的数据进行清洗,并且对采集到的相关信息进行解析,通过soup的select_one对网页源代码进行匹配从而找出其中想要的数据存到字段中,最后以csv格式文件保存。

难点:58同城具有反爬机制,提取58同城网页结构信息,和相关信息的提取,并且在爬取价格信息时,发现价格字段经过base64加密。

二、主题页面的结构特征分析(15分)

1.主题页面的结构特征

包含了二手房的区域、总价、面积、厅室等信息。

2.Htmls页面解析

通过F12,查看网页源代码,查询相关信息。

3.节点(标签)查找方法与遍历方法(必要时画出节点树结构)

通过requests保持浏览器的登录状态,beautifulsoup对网页中的数据进行清洗,并且对采集到的相关信息进行解析,通过soup的select_one对网页源代码进行匹配从而找出其中想要的数据存到字段中。

三、网络爬虫程序设计(60分)

爬虫程序主体要包括以下各部分,要附源代码及较详细注释,并在每部分程序后面提供输出结果的截图。

1 importrequests2 importtime3 importcsv4 importre5 importbs46 importos7 importlogging8 importrandom9 importbase6410 from urllib.parse importurljoin11

12 from fontTools.ttLib importttFont, BytesIO13

14 #用来维持cookie,保持登录状态

15 session =requests.Session()16 #设置用户代理和cookie,使58认为这是从浏览器去访问它的

17 session.headers["User-Agent"] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15'

18 session.headers["Cookie"] = 'commontopbar_ipcity=gllosangeles%7C%E6%B4%9B%E6%9D%89%E7%9F%B6%7C1; commontopbar_new_city_info=10291%7C%E5%8D%97%E5%B9%B3%7Cnp; f=n; als=0; wmda_session_id_11187958619315=1576494942212-5c6d2465-19f1-0b3e; wmda_visited_projects=%3B6333604277682%3B11187958619315; f=n; 58tj_uuid=fef049a4-34c6-42fb-9b5c-032c2de6a7a2; init_refer=; new_session=1; new_uv=1; spm=; utm_source=; wmda_new_uuid=1; wmda_session_id_6333604277682=1576494927127-6600b0cd-c4a6-6ce6; wmda_uuid=a862cf3bd206f96cab08a1c24772181b; JSESSIONID=CDCB6D81046D5476833EE19B97BEA52A; bj58_new_uv=1; bj58_id58s="OD1mblFRc2xpZkxJNTA0MA=="; id58=c5/nn12E1PkwRuPacj8eAg=='

19

20 #设置一个列表,存储地址、结果以及爬过的网址

21 queue = [('https://np.58.com/ershoufang/pn2/', 'list', 0)]22 results =dict()23 footprint =dict()24 fields =set()25

26 #反复爬取页面直到爬取完所有页面

27 while len(queue) >0:28 #取出一个任务

29 url, kind, retry =queue.pop(0)30

31 if url infootprint:32 continue

33 #爬取网页

34 response =session.get(url)35 if response.status_code != 200:36 #如果状态码不是200就是没有爬到,先跳过

37 print("failed to crawl {}".format(url))38 #重试5次

39 if retry < 5:40 queue.append((url, kind, retry +1))41 continue

42

43 if response.text.find("验证码") >=0:44 #遭遇验证码时休眠一会,避免因过于频繁操作而被认为是机器人

45 print("need captcha, rest for a while")46 #比如说,十分钟

47 time.sleep(10*60)48

49 if retry < 5:50 #如果重试次数小于5的话,输出访问的url,类型,并让重试次数+1,并随机的休眠5-7秒

51 #为了定位错误和输出错误信息

52 queue.append((url, kind, retry + 1))53 time.sleep(2 + random.randint(3,5))54 continue

55

56

57 print("crawled url {}".format(url))58

59 #分析页面

60 soup = bs4.BeautifulSoup(response.text, "html.parser")61 #记录一下自己爬过了

62 footprint[url] =True63

64 if kind == "list":65 #如果是房屋列表页面

66 #把有关的链接放入队列中

67 for link in soup.find_all("a"):68 new_url = urljoin(url, link.attrs.get("href", ""))69 new_url = new_url.split("?")[0]70

71 #pn 打头的都是目录,数字开头的都是房屋信息页

72 if new_url.startswith("https://np.58.com/ershoufang/pn") and new_url not infootprint:73 print("append list page into queue: {}".format(new_url))74 queue.append((new_url, "list", 0))75 elif new_url.startswith("https://np.58.com/ershoufang/") and new_url not in footprint and re.match("https://np.58.com/ershoufang/[0-9]{3,}", new_url):76 print("append detail page into queue: {}".format(new_url))77 queue.append((new_url, "detail", 0))78 else:79 #如果是房屋信息页面

80 #先提取字体

81 fontBase64 = re.search("'data:application/font-ttf;charset=utf-8;base64,(?P[^']*)'", response.text)82 #如果房屋信息页面存在字体

83 if fontBase64 is not None and 'data' infontBase64.groupdict():84 #从base64恢复字体文件

85 fontRaw = base64.b64decode(fontBase64.groupdict()['data'])86 #解析字体

87 font =ttFont.TTFont(BytesIO(fontRaw))88

89 webdata =response.text90

91 #找到字体对应数字的关系

92 for code, name infont.getBestCmap().items():93 #用chr把对应的数字转换成html的unicode字符

94 ch = '{:x};'.format(code)95 #58偷懒,glyph最后一位就是实际的数字

96 digit = int(name[-2:]) - 1

97 #还原网页里面的所有unicode字符串

98 webdata =webdata.replace(ch, str(digit))99

100 #重新解析页面

101 soup = bs4.BeautifulSoup(webdata, "html.parser")102 else:103 print("shit, font is not avaible in", url)104

105 results[url] ={106 'title': soup.select_one(".main-wrap .house-title h1").text.replace("\n", "").replace(" ", ""),107 'image': soup.select_one("#smainPic")["src"],108 'sumPrice': soup.select_one(".main-wrap .price").text.replace("\n", "").replace(" ", ""),109 'unitPrice': soup.select_one(".main-wrap .unit").text.replace("\n", "").replace(" ", ""),110 'houseRoom': soup.select_one(".room .main").text.replace("\n", "").replace(" ", ""),111 'houseFloor': soup.select_one(".room .sub").text.replace("\n", "").replace(" ", ""),112 'houseArea': soup.select_one(".area .main").text.replace("\n", "").replace(" ", ""),113 'houseFurnishing': soup.select_one(".area .sub").text.replace("\n", "").replace(" ", ""),114 'houseTorward': soup.select_one(".toward .main").text.replace("\n", "").replace(" ", ""),115 'houseBuildAt': soup.select_one(".toward .sub").text.replace("\n", "").replace(" ", ""),116 'houseAddress': soup.find(attrs={'class': 'c_999'}, text='位置:').find_next_sibling().text.replace("\n", '').replace(' ', ''),117 'housePrice': soup.find(attrs={'class': 'c_999'}, text='房屋总价').find_next_sibling().text.replace("\n", '').replace(' ', ''),118 }119 fields.update(results[url].keys())120

121 #如果队列里面还有东西,停一下

122 ifqueue:123 time.sleep(2 + random.randint(3, 5))124

125 #done

126 #存储数据

127

128 writer = csv.DictWriter(open("output.csv", "w+"), tuple(fields))129 writer.writeheader()130 writer.writerows(list(results.values()))

1.产生的文件

2.运行结果

(1)在终端上

(2)在csv中

1.数据爬取与采集

response =session.get(url)if response.status_code != 200:#如果状态码不是200就是没有爬到,先跳过

print("failed to crawl {}".format(url))#重试5次

if retry < 5:

queue.append((url, kind, retry+1))continue

if response.text.find("验证码") >=0:#遭遇验证码时休眠一会,避免因过于频繁操作而被认为是机器人

print("need captcha, rest for a while")#比如说,十分钟

time.sleep(10*60)if retry < 5:#如果重试次数小于5的话,输出访问的url,类型,并让重试次数+1,并随机的休眠5-7秒

#为了定位错误和输出错误信息

queue.append((url, kind, retry + 1))

time.sleep(2 + random.randint(3,5))continue

print("crawled url {}".format(url))

2.数据分析

#分析页面

soup = bs4.BeautifulSoup(response.text, "html.parser")#记录一下自己爬过了

footprint[url] =Trueif kind == "list":#如果是房屋列表页面

#把有关的链接放入队列中

for link in soup.find_all("a"):

new_url= urljoin(url, link.attrs.get("href", ""))

new_url= new_url.split("?")[0]#pn 打头的都是目录,数字开头的都是房屋信息页

if new_url.startswith("https://np.58.com/ershoufang/pn") and new_url not infootprint:print("append list page into queue: {}".format(new_url))

queue.append((new_url,"list", 0))elif new_url.startswith("https://np.58.com/ershoufang/") and new_url not in footprint and re.match("https://np.58.com/ershoufang/[0-9]{3,}", new_url):print("append detail page into queue: {}".format(new_url))

queue.append((new_url,"detail", 0))else:#如果是房屋信息页面

#先提取字体

fontBase64 = re.search("'data:application/font-ttf;charset=utf-8;base64,(?P[^']*)'", response.text)#如果房屋信息页面存在字体

if fontBase64 is not None and 'data' infontBase64.groupdict():#从base64恢复字体文件

fontRaw = base64.b64decode(fontBase64.groupdict()['data'])#解析字体

font =ttFont.TTFont(BytesIO(fontRaw))

webdata=response.text#找到字体对应数字的关系

for code, name infont.getBestCmap().items():#用chr把对应的数字转换成html的unicode字符

ch = '{:x};'.format(code)#58偷懒,glyph最后一位就是实际的数字

digit = int(name[-2:]) - 1

#还原网页里面的所有unicode字符串

webdata =webdata.replace(ch, str(digit))#重新解析页面

soup = bs4.BeautifulSoup(webdata, "html.parser")else:print("shit, font is not avaible in", url)

3.对数据进行清洗和处理

results[url] ={'title': soup.select_one(".main-wrap .house-title h1").text.replace("\n", "").replace(" ", ""),'image': soup.select_one("#smainPic")["src"],'sumPrice': soup.select_one(".main-wrap .price").text.replace("\n", "").replace(" ", ""),'unitPrice': soup.select_one(".main-wrap .unit").text.replace("\n", "").replace(" ", ""),'houseRoom': soup.select_one(".room .main").text.replace("\n", "").replace(" ", ""),'houseFloor': soup.select_one(".room .sub").text.replace("\n", "").replace(" ", ""),'houseArea': soup.select_one(".area .main").text.replace("\n", "").replace(" ", ""),'houseFurnishing': soup.select_one(".area .sub").text.replace("\n", "").replace(" ", ""),'houseTorward': soup.select_one(".toward .main").text.replace("\n", "").replace(" ", ""),'houseBuildAt': soup.select_one(".toward .sub").text.replace("\n", "").replace(" ", ""),'houseAddress': soup.find(attrs={'class': 'c_999'}, text='位置:').find_next_sibling().text.replace("\n", '').replace(' ', ''),'housePrice': soup.find(attrs={'class': 'c_999'}, text='房屋总价').find_next_sibling().text.replace("\n", '').replace(' ', ''),

}

fields.update(results[url].keys())#如果队列里面还有东西,停一下

ifqueue:

time.sleep(2 + random.randint(3, 5))

4.数据持久化

writer = csv.DictWriter(open("output.csv", "w+"), tuple(fields))

writer.writeheader()

writer.writerows(list(results.values()))

四、结论(10分)

1.经过对主题数据的分析与可视化,可以得到哪些结论?

通过对58同城的页面爬取与分析,了解到南平房产出售的位置、总价、面积、厅室等等相关信息。

2.对本次程序设计任务完成的情况做一个简单的小结。

通过本次爬虫的设计,对python的爬虫有了进一步的了解,也加深了requests、beautifulsoup的使用,并且采集到了58同城房产相关信息,学习到了如何爬取网页信息,及如何对加密信息进行解密,有了很大的收获。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值