Python爬虫之路-爬取北、上、广租房信息

链家网
在实现爬取北、上、广租房信息时,需要使用fake_useragent、asyncio、aiohttp、requests、lxml以及pandas模块。

各模块关键技术如下:

1.fake_useragent模块
fake_useragent模块是第三方模块,所以在使用前需要先通过以下命令进行模块的安装:

pip install fake-useragent

模块安装完成以后,首先需要导入fake_useragent模块中的UserAgent类。代码如下:

from fake_useragent import UserAgent  # 导入伪造头部信息的模块

(1)调用fake_useragent模块中UserAgent类的random属性可以实现随机获取浏览器的头部信息。语法格式如下:

UserAgent().random

实现随机获取浏览器头部信息的示例代码如下:
在这里插入图片描述

02	from fake_useragent import UserAgent  # 导入伪造头部信息的模块
03	for i in range(5):
04	    # 打印随机生成浏览器头部信息
05	    print({
    
    "User-Agent": UserAgent().random})

2.asyncio模块
asyncio模块主要用于执行异步io的处理工作,异步和同步是相对的,在执行一些耗时的I/O操作时,只发出I/O指令,并不会等待I/O的结果,在此期间去执行其它事情,以此提高效率。
asyncio模块是python的内置模块,所以在使用前需要使用import导入该模块。代码如下:

import asyncio

(1)协程是编写异步应用的推荐方式,需要通过async/await语法进行声明。例如,实现并发运行多个协程可以参考以下示例代码:

06	import asyncio           # 导入异步IO模块
07	import time              # 导入时间模块
08	async def asyncio_demo(delay, what):   # 创建异步函数
09	    await asyncio.sleep(delay)          # 等待指定时间
10	    print(what)
11	async def main():                      # 异步主函数
12	    # 创建协程任务1
13	    task1 = asyncio.create_task(
14	        asyncio_demo(1, 'hello'))
15	    # 创建协程任务2
16	    task2 = asyncio.create_task(
17	        asyncio_demo(2, 'world'))
18	    print("start_time",time.strftime('%X'))
19	    await task1                         # 调用协程任务1
20	    await task2                         # 调用协程任务2
21	    print("end_time",time.strftime('%X'))
22	asyncio.run(main())                      # 并发运行多个协程任务

运行结果如下:
start_time 11:16:52
hello
world
end_time 11:16:54

(2)在asyncio模块中提供了get_event_loop()方法,用于获取事件循环的实例对象,然后通过调用实例对象中的run_until_complete()方法来运行任务并等待任务完成。示例代码如下:

23	import asyncio      # 导入异步IO模块
24	async def hello1(a):           # 创建异步函数
25	    await asyncio.sleep(a)     # 等待指定时间
26	    print('执行任务!')
27	loop = asyncio.get_event_loop() # 创建事件循环实例
28	start_time = loop.time()        # 开始时间
29	print('开始时间:',start_time)               # 打印开始时间
30	loop.run_until_complete(hello1(1)) # 运行任务函数
31	end_time = loop.time()           # 结束时间
32	print('结束时间:',end_time)                  # 打印结束时间
33	print('间隔时间:',end_time - start_time)     # 打印间隔时间

运行结果如下:
开始时间: 621910.312
执行任务!
结束时间: 621911.312
间隔时间: 1.0

3.aiohttp模块
aiohttp模块是异步网络请求模块,该模块需要和asyncio模块配合使用。aiohttp模块是python的内置模块,所以在使用前需要使用import导入该模块。代码如下:

import aiohttp

在实现异步网络请求时,首先需要定义一个异步请求的函数,在该函数中创建ClientSession()的实例对象,然后通过该实例对象调用get()方法即可实现异步网络请求。示例代码如下:

34	import asyncio  # 异步io模块
35	import aiohttp  # 异步网络请求模块
36	async def request(url):  # 异步网络请求的方法
37	    async with aiohttp.ClientSession() as session:  # 创建异步网络请求对象
38	        try:
39	            # 根据传递的地址发送网络请求
40	            async with session.get(url,timeout=3) as response:
41	                if response.status == 200:  # 如果请求码为200说明请求成功
42	                    print(response.status)  # 打印请求码
43	        except Exception as e:
44	            print(e.args)  # 打印异常信息
45	if __name__ == '__main__':
46	    loop = asyncio.get_event_loop()
47	    loop.run_until_complete(request('https://www.baidu.com'))

运行结果如下:
200

4.requests模块
requests模块用于实现发送网络请求,requests模块是第三方模块,所以在使用前需要先通过以下命令进行模块的安装:
pip install requests
模块安装完成以后,首先需要导入requests模块,然后调用对应的get()函数即可实现网络请求的发送功能。代码如下:

48	import requests             # 导入模块
49	response = requests.get('http://www.baidu.com')
50	print(response.status_code)  # 打印状态码

5.lxml模块
lxml模块是一个解析库,用于解析HTML和XML,支持XPath解析方式,并且解析效率比较高。lxml模块是第三方模块,所以在使用前需要先通过以下命令进行模块的安装:
pip3 install lxml
在实现解析HTML代码时需要导入lxml模块下的etree子模块,然后创建etree子模块中的HTML实例对象,此时即可调用xpath()方法获取html代码中的指定内容。代码如下:

51	from lxml import etree     # 导入lxml解析html的模块
52	# 模拟html
53	html_text = '''
54	<html>
55	<head>
56	<title>我的第一个 HTML 页面</title>
57	</head>
58	<body>
59	<p>文件内容</p>
60	<p>详细介绍</p>
61	</body>
62	</html>
63	'''
64	html = etree.HTML(html_text)   # 解析html代码
65	title_text = html.xpath('//title/text()')  # 获取标题内容
66	print(title_text[0])                       # 打印提取的标题内容

运行结果如下:
我的第一个 HTML 页面

6.pandas模块
pandas模块用于实现数据的统计与分析工作,该模块为第三方模块所以在使用前需要先通过以下命令进行模块的安装:
pip3 install pandas
在pandas模块中DataFrame对象式比较常用的,在创建DataFrame对象时,需要通过字典来实现。其中每列的名称为键,而每个键对应的是一个数组,这个数组作为值。代码如下:

67	import pandas  # 导入数据统计模块
68	data = {
    
    'A': [1, 2, 3, 4, 5],
69	        'B': [6, 7, 8, 9, 10],
70	        'C':[11,12,13,14,15]}
71	data__frame = pandas.DataFrame(data)  # 创建DataFrame对象
72	print(data__frame)  # 打印DataFrame对象内容

运行结果如下:
A B C
0 1 6 11
1 2 7 12
2 3 8 13
3 4 9 14
4 5 10 15

实例代码:

'''
爬取链家网
'''

from fake_useragent import UserAgent  # 导入伪造头部信息的模块
import asyncio  # 异步io模块
import aiohttp  # 异步网络请求模块
import requests  # 导入网络请求模块
from lxml import etree  # 导入lxml解析html的模块
import pandas  # 导入pandas模块


class HomeSpider():  # 链家爬虫的类
    def __init__(self):  # 初始化
        self.data = []  # 创建数据列表
        self.headers = {
    
    "User-Agent": UserAgent().random}  # 随机生成浏览器头部信息

    async def request(self, url):  # 异步网络请求的方法
        async with aiohttp.ClientSession() as session:  # 创建异步网络请求对象
            try:
                # 根据传递的地址发送网络请求
                async with session.get(url, headers=self.headers, timeout=3) as response:
                    print(response.status)
                    if response.status == 200:  # 如果请求码为200说明请求成功
                        result = await response.text()  # 获取请求结果中的文本代码
                        return result
            except Exception as e:
                print(e.args)  # 打印异常信息

    def get_page_all(self, city):  # 请求一次,获取租房信息的所有页码
        city_letter = self.get_city_letter(city)  # 获取城市对应的字母
        url = 'https://{}.lianjia.com/zufang/ab200301001000rco11rt200600000001rs{}/'.format(city_letter, city)
        response = requests.get(url, headers=self.headers)  # 发送网络请求
        if response.status_code == 200:
            html = etree.HTML(response.text)  # 创建一个XPath解析对象
            # 获取租房信息的所有页码
            page_all = html.xpath('//*[@id="content"]/div[1]/div[2]/@data-totalpage')[0]
            print('租房信息总页码获取成功!')
            return int(page_all) + 1
        else:
            print('获取租房信息所有页码的请求未成功!')

    # 解析数据
    async def parse_data_all(self, page_all, city):
        for i in range(1,page_all):  # 根据租房信息的总页码,分别对每一页信息发送网络请求
            city_letter = self.get_city_letter(city)  # 获取城市对应的字母
            url = 'https://{}.lianjia.com/zufang/ab200301001000pg{}rco11rt200600000001rs{}/'.format(city_letter,i, city)
            html_text = await self.request(url)  # 发送网络请求,获取html代码
            html = etree.HTML(html_text)  # 创建一个XPath解析对象
            print('获取'+url+'页信息!')
            title_all = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[1]/a/text()')  # 获取每页中所有标题
            big_region_all = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/a[1]/text()')  # 获取每页中所有大区域
            small_region_all = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/a[2]/text()')  # 获取每页中所有小区域
            square_all = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/text()[5]')  # 获取每页中所有房子的面积
            floor_all = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/span/text()[2]')  # 获取每页中所有房子的楼层
            price_all = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/span/em/text()')  # 获取每页中所有房子的价格
            title_list = self.remove_spaces(title_all)  # 删除标题信息中的空格与换行符
            region_list = self.combined_region(big_region_all, small_region_all)  # 组合后的区域信息
            square_list = self.remove_spaces(square_all)  # 删除面积信息中的空格与换行符
            floor_list = self.remove_spaces(floor_all)  # 删除楼层信息中的空格与换行符
            price_list = self.remove_spaces(price_all)  # 删除价格信息中的空格与换行符
            # 每页数据
            data_page = {
    
    'title': title_list,
                         'region': region_list,
                         'price': price_list,
                         'square': square_list,
                         'floor': floor_list}
            print('写入第'+str(i)+'页数据!')
            df = pandas.DataFrame(data_page)              # 创建DataFrame数据对象
            df.to_csv('{}租房信息.csv'.format(city),mode='a', encoding='utf_8_sig',index=None)  # 写入每页数据


    # 删除字符串中的空格与换行符
    def remove_spaces(self, info):
        info_list = []  # 保存去除空格后的字符串
        for i in info:  # 循环遍历包含空格信息
            x=i.replace(' ', '').replace('\n', '')
            if x =='':
                pass
            else:
                info_list.append(x)  # 将去除空格后的字符串添加至列表中
        return info_list  # 返回去除空格后的信息

    # 获取北、上、广城市名称对应的字母
    def get_city_letter(self, city_name):
        city_dict = {
    
    '北京': 'bj', '上海': 'sh', '广州': 'gz'}
        return city_dict.get(city_name)  # 返回城市名称对应的英文字母

    # def get_city_letter(self, city_name):
    #     city_dict = {'北京': 'bj', '上海': 'sh', '广州': 'gz','深圳':'sz'}
    #     return city_dict.get(city_name)  # 返回城市名称对应的英文字母

    # 将大区域小区域合并
    def combined_region(self, big_region, small_region):
        region_list = []  # 保存组合后的区域信息
        # 循环遍历大小区域,并将区域组合
        for a, b in zip(big_region, small_region):
            region_list.append(a + '-' + b)
        return region_list

    # 启动异步
    def start(self, page_all, city):
        loop = asyncio.get_event_loop()      # 创建loop对象
        # 开始运行
        loop.run_until_complete(self.parse_data_all(page_all, city))


if __name__ == '__main__':
    input_city = input('请输入需要下载租房信息的城市名称!')
    home_spider = HomeSpider()  # 创建爬虫类对象
    page_all = home_spider.get_page_all(input_city)  # 获取所有页码
    print(page_all)    # 打印所有页码信息
    home_spider.start(page_all, input_city)          # 启动爬虫程序

猜你喜欢

转载自blog.csdn.net/Yuyu920716/article/details/114176764