爬虫笔记16——异步爬取二手汽车数据去重存入MySQL

需要用到的库

bash 复制代码
#异步数据库
pip install aiomysql
#reids数据库进行去重
pip install redis
#用hashlib进行md5加密
pip install hashlib
#基于异步IO的网络请求库
pip install aiohttp
#xpath获取静态页面数据
pip install lxml

目标网站

目标网站:https://www.che168.com/china/a0_0msdgscncgpi1ltocsp1exf4x0/?pvareaid=102179#currengpostion

获取汽车具体数据的api:https://cacheapigo.che168.com/CarProduct/GetParam.ashx?specid={}

其中{}方便使用format进行格式化

思路分析

1、要获取详细的汽车数据,需要在首页点击某个汽车进入到详情页面数据,这个过程会携带该汽车类型的specid跳转到详情数据页面,并请求对应的接口返回具体数据信息。

2、那我们需要在这个首页的罗列数据提取到每个汽车对应的specid,因为这是一个静态页面,使用xpath获取每个汽车的specid。

3、在详情数据页面,获取汽车的具体数据需要分析对应的接口进行请求,这个请求的过程需要携带前面获取到的specid。

4、最后获取数据成功进行提取,去重,存储即可

代码示例

值得注意的是:这个网页有反爬机制,他的页面编码格式会切换,如果返回的编码格式是UTF-8-SIG,是获取不到网页数据的,也就是提取不到specid,那后面也就获取不了汽车详细数据,所以我们要验证页面编码格式,这里要用到chardet包

bash 复制代码
pip install chardet

代码实现:

python 复制代码
# -*- coding: utf-8 -*-
# @Time:      2024/06/24 12:51
# @File:       二手车.py

import aiomysql
import aiohttp
import redis
import hashlib
import chardet
import asyncio
from lxml import etree

class SpiderCar:
    def __init__(self):
        self.redis = redis.Redis()
        self.url = 'https://www.che168.com/china/a0_0msdgscncgpi1ltocsp{}exf4x0/?pvareaid=102179#currengpostion'
        self.api_url = 'https://cacheapigo.che168.com/CarProduct/GetParam.ashx?specid={}'
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
        }

    async def get_car_id(self, page, session, pool):
        async with session.get(self.url.format(page), headers=self.headers) as response:
            content = await response.read()
            encoding = chardet.detect(content)["encoding"]
            if encoding == 'GB2312':
                html = content.decode('gbk')
            else:
                html = content.decode(encoding)
                print('被反爬了...')

            tree = etree.HTML(html)
            car_id_list = tree.xpath("//ul[@class='viewlist_ul']/li/@specid")
            if car_id_list:
                car_info_tasks = [asyncio.create_task(self.get_car_info(car_id, session, pool)) for car_id in car_id_list]
                await asyncio.wait(car_info_tasks)

    async def get_car_info(self, car_id, session, pool):
        async with session.get(self.api_url.format(car_id), headers=self.headers) as response:
            car_info = await response.json()
            # print(car_info)
            if car_info["result"]["paramtypeitems"]:
                item = dict()
                item['name'] = car_info['result']['paramtypeitems'][0]['paramitems'][0]['value']
                item['price'] = car_info['result']['paramtypeitems'][0]['paramitems'][1]['value']
                item['brand'] = car_info['result']['paramtypeitems'][0]['paramitems'][2]['value']
                item['altitude'] = car_info['result']['paramtypeitems'][1]['paramitems'][2]['value']
                item['breadth'] = car_info['result']['paramtypeitems'][1]['paramitems'][1]['value']
                item['length'] = car_info['result']['paramtypeitems'][1]['paramitems'][0]['value']
                await self.save_car_info(item, pool)

    @staticmethod
    def md5_hash(item):
        hash_obj = hashlib.md5()
        hash_obj.update(str(item).encode())
        return hash_obj.hexdigest()

    async def save_car_info(self, item, pool):
        async with pool.acquire() as connect:
            async with connect.cursor() as cursor:
                hash_item = self.md5_hash(item)
                obj = self.redis.sadd('car_info:filter', hash_item)
                if obj:
                    sql = """
                        insert into car_info value (
                            %s, %s, %s, %s, %s, %s, %s
                        );
                    """
                    try:
                        await cursor.execute(sql, (0, item['name'], item['price'], item['brand'], item['altitude'], item['breadth'], item['length']))
                        await connect.commit()
                        print('插入成功')
                    except Exception as e:
                        print('插入失败!', e)
                        await connect.rollback()
                else:
                    print('数据重复,跳过该条数据')

    async def main(self):
        async with aiomysql.create_pool(user='root', password='root', db='py_spider') as pool:
            async with pool.acquire() as connect:
                async with connect.cursor() as cursor:
                    # sql = """
                    #     create table if not exists car_info (
                    #         id int primary key auto_increment,
                    #         name varchar(100),
                    #         price varchar(100),
                    #         brand varchar(100),
                    #         altitude varchar(100),
                    #         breadth varchar(100),
                    #         length varchar(100)
                    #     )
                    # """
                    # await cursor.execute(sql)
                    # 创建表
                    create_table_sql = """
                                           create table car_info(
                                               id int primary key auto_increment,
                                               name varchar(100),
                                               price varchar(100),
                                               brand varchar(100),
                                               altitude varchar(100),
                                               breadth varchar(100),
                                               length varchar(100)
                                           );
                                       """

                    # 在异步代码中必须先要检查表是否存在, 直接使用if not语句无效
                    check_table_query = "show tables like 'car_info'"
                    result = await cursor.execute(check_table_query)  # 如果表存在返回1 不存在返回0
                    if not result:
                        await cursor.execute(create_table_sql)

            async with aiohttp.ClientSession() as session:
                car_id_tasks = [asyncio.create_task(self.get_car_id(page, session, pool)) for page in range(1, 100)]
                await asyncio.wait(car_id_tasks)


if __name__ == '__main__':
    spider_car = SpiderCar()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(spider_car.main())
相关推荐
遇印记1 小时前
大二java学习笔记:二维数组
java·笔记·学习
bnsarocket3 小时前
Verilog和FPGA的自学笔记6——计数器(D触发器同步+异步方案)
笔记·fpga开发·verilog·自学·硬件编程
LK_074 小时前
【Open3D】Ch.3:顶点法向量估计 | Python
开发语言·笔记·python
li星野4 小时前
打工人日报#20251011
笔记·程序人生·fpga开发·学习方法
摇滚侠4 小时前
Spring Boot 3零基础教程,yml配置文件,笔记13
spring boot·redis·笔记
QT 小鲜肉4 小时前
【个人成长笔记】在Ubuntu中的Linux系统安装 anaconda 及其相关终端命令行
linux·笔记·深度学习·学习·ubuntu·学习方法
QT 小鲜肉4 小时前
【个人成长笔记】在Ubuntu中的Linux系统安装实验室WIFI驱动安装(Driver for Linux RTL8188GU)
linux·笔记·学习·ubuntu·学习方法
急急黄豆4 小时前
MADDPG学习笔记
笔记·学习
Chloeis Syntax5 小时前
栈和队列笔记2025-10-12
java·数据结构·笔记·
QZ_orz_freedom6 小时前
学习笔记--文件上传
java·笔记·学习