前言
笔者在做一个 N L P NLP NLP 项目时,需要识别小区名,因此想要查询网上是否有相关的数据集。经过一番搜索后一无所获…从而决定自己来爬取这份数据。
由于爬取网站的内容信息之后可能会发生变更,因此此处给出具体的爬虫思路,便于应对之后的网站结构变更。
方法
1. 爬取网站的确定
既然是爬虫,那首先就需要确定爬取网站。这时候就需要思考哪些网站可能会涉及小区名字?
- 国家统计网?经过一番搜索,没找到…
- 房屋中介网?√
因此我们可以选取比较知名的房屋中介网来进行小区名爬取,此处选取的网站是链家,原因是该网站有专门的小区板块。链家
2. 网站结构分析
由于链家对于不同城市,建立了不同的网页,因此我们以北京的网页举例。
第一步,选中所有选项,查看当前网址。
https://bj.lianjia.com/xiaoqu/pg1y1y2y3y4y5p1p2p3p4p5p6/
多查看几个网页,就可以发现上述网址的共性,bj
是城市缩写,pg1
是页面编号。
第二步,查看当前网页的 H t m l Html Html,分析元素结构。
执行下述代码,即可查看该页面解析过后的格式。
from bs4 import BeautifulSoup
import requests
url = 'https://{}.lianjia.com/xiaoqu/pg{}y1y2y3y4y5p1p2p3p4p5p6/'
response = requests.get("https://bj.lianjia.com/xiaoqu/pg1y1y2y3y4y5p1p2p3p4p5p6/").text
soup = BeautifulSoup(response, 'lxml')
print(soup)
对上述输出的内容稍加搜索,即可发现小区名所在页面结构如下。可以发现在下面两种格式中均出现了小区名,一个在图片结构中,另一个是页面显示的文字,我们选取图片结构对小区信息进行提取。
<img alt="新龙城" class="lj-lazy" data-original="https://image1.ljcdn.com/hdic-resblock/025cb5ab-9460-417e-bd4b-78868900d752.jpg.232x174.jpg" src="https://s1.ljcdn.com/feroot/pc/asset/img/blank.gif?_v=20200331145335"/>
<a href="https://bj.lianjia.com/xiaoqu/1111027381003/" target="_blank">新龙城</a>
因此我们增加一行代码来提取 i m g img img 标签中的信息并输出。
text_list = soup.find_all('img', class_="lj-lazy")
for item in text_list:
print(item['alt'])
# 输出结果:
# 首开康乃馨城
# 农光里
# 理想家园
# 华贸城
# 住欣家园
# 远洋山水
# 旗胜家园
# 小南庄社区
# ...
第三步,构造网站 u r l url url
首先我们考虑页面如何构造。这个难度不大,我们只需要 f o r for for 一遍页面编号即可。这个网站有一个特点在于,网站上可能只显示了 30 30 30 页,但是实际上可能有 100 100 100 页…并且假如第 100 100 100 页是最后一页,那么第 101 101 101 页的内容将与第 100 100 100 页保持一致。因此我们根据当前网页是否与上一个网页一致,来判断当前网站爬取是否结束。
def get_housing_estate():
fo = open("data/housing_estate.txt", "w")
# 设置初始值
last = BeautifulSoup(requests.get(url.format("wz", 1)).text, 'lxml').find_all('img', class_="lj-lazy")
for city in address_list:
for page in range(1, 500):
print(city, page)
# 创建bs对象
try:
response = requests.get(url.format(city, page)).text
except:
# 页面出错,则更换下一个城市
break
soup = BeautifulSoup(response, 'lxml') # 使用到了lxml解析库
text_list = soup.find_all('img', class_="lj-lazy")
# 出现重复页面
if text_list == last:
break
last = text_list
# 写入文件
for item in text_list:
fo.write(item['alt'] + '\n')
接下来就要考虑城市简写如何爬取了。我们可以根据这个网站中提供的城市分类来对网站进行爬取。
然后问题就变成了这个网站的城市名如何爬取呢?流程与上述三步一致,爬取网页,分析结构,提取数据。此处就不再具体展开分析了,直接提供代码,大家也可以先不看代码,自己尝试爬取。
def get_address():
response = requests.get("https://www.lianjia.com/city/").text
soup = BeautifulSoup(response, 'lxml')
text_list = soup.find_all('a')
for item in text_list:
if type(item.get('href')) == str and item.get('href')[0] == 'h':
value = item.get('href').split("https://")[1].split('.')[0]
if value not in address_list:
address_list.append(value)
if value == 'yw':
break
print(address_list)
3. 完整代码
在总代码中,笔者进行了两项处理。
- 使用 t r y − c a t c h try-catch try−catch 的方式保证页面 u r l url url 出错时,项目不会崩溃
- 通过记录前一个页面的方式,保证爬取页面时能及时退出
from bs4 import BeautifulSoup
import requests
address_list = []
url = 'https://{}.lianjia.com/xiaoqu/pg{}y1y2y3y4y5p1p2p3p4p5p6/'
def get_address():
response = requests.get("https://www.lianjia.com/city/").text
soup = BeautifulSoup(response, 'lxml')
text_list = soup.find_all('a')
for item in text_list:
if type(item.get('href')) == str and item.get('href')[0] == 'h':
value = item.get('href').split("https://")[1].split('.')[0]
if value not in address_list:
address_list.append(value)
if value == 'yw':
break
print(address_list)
def get_housing_estate():
fo = open("data/housing_estate.txt", "w")
last = BeautifulSoup(requests.get(url.format("wz", 1)).text, 'lxml').find_all('img', class_="lj-lazy")
for city in address_list:
for page in range(1, 500):
print(city, page)
# 创建bs对象
try:
response = requests.get(url.format(city, page)).text
except:
break
soup = BeautifulSoup(response, 'lxml') # 使用到了lxml解析库
text_list = soup.find_all('img', class_="lj-lazy")
# 出现重复页面
if text_list == last:
break
last = text_list
# 写入文件
for item in text_list:
fo.write(item['alt'] + '\n')
def main():
get_address()
get_housing_estate()
if __name__ == "__main__":
main()
后记
代码总体逻辑不难,总长度也不长,但可惜由于数据量太大,笔者爬了快
1
h
1h
1h 了也还没结束…使用者需有一定的耐心…
爬取过程仍在继续… (〃・o・〃)