当前位置: 首页 > article >正文

Python爬取机车网车型数据并存入Mysql数据库

结果展示(文末附完整代码):

一、引言

        在当今数字化时代,数据对于各个领域的重要性不言而喻。对于机车行业而言,获取丰富的机车品牌、车型及详细信息数据,能够为市场分析、消费者研究等提供有力支持。本文将详细介绍一个使用 Python 编写的机车数据爬虫项目,该爬虫能够从特定机车网站抓取机车品牌、车型及其详细信息,并将数据存储到 MySQL 数据库中(同时也提供了 MongoDB 存储的部分代码示例)。

二、项目概述

        本项目旨在实现一个自动化的机车数据采集工具,通过对具体机车网站的爬取,获取机车品牌列表,进一步深入到每个品牌的车型页面,最终抓取车型的详细信息页面数据。整个过程涵盖了页面请求、数据解析以及数据存储等关键环节,下面将逐步展开介绍。

三、代码实现细节

(一)类的初始化(__init__方法)

        在JiChe类的初始化方法中,首先设定了要爬取的基础 URL,即http://www.jiche.com/pinpai/,这是整个爬虫的起始点。同时,定义了请求头信息,模拟浏览器发送请求,避免被网站识别为爬虫而拒绝访问。在本次代码中,使用的是常见的 Chrome 浏览器的 User-Agent 信息。

        对于数据库连接部分,代码中连接到了本地的 MySQL 数据库。设置了主机地址为127.0.0.1,端口号3306,用户名root,密码921108,数据库名称为fjj,并创建了数据库游标,以便后续执行 SQL 语句操作数据库。虽然代码中也包含了连接 MongoDB 的部分注释代码,但本文主要聚焦于 MySQL 数据库的操作与讲解。

class JiChe(object):
    def __init__(self):
        """
        初始化 JiChe 类的实例。

        在这里设置了要爬取的基础 URL、请求头信息,以及连接到 MySQL 数据库所需的参数,
        并创建了数据库游标。
        """
        self.url = 'http://www.jiche.com/pinpai/'
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                          "Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.37 "
        }
        # # 连接 MongoDB
        # self.mongo_client = MongoClient('localhost', 27017)
        # self.mongo_db = self.mongo_client['your_database_name']
        # self.mongo_collection = self.mongo_db['your_collection_name']
        self.db = pymysql.Connect(
            host='127.0.0.1',
            port=3306,
            user='root',
            password='921108',
            db='fjj'
        )
        self.cursor = self.db.cursor()

(二)获取页面内容(get_page_content方法)

   get_page_content方法负责发送 HTTP GET 请求获取指定 URL 的页面内容。它接收一个url参数,即要请求的页面地址。在方法内部,使用requests库发送请求,并根据请求头信息进行伪装。同时,为了确保正确解析页面中的中文等字符,将响应的编码设置为utf8。如果请求过程中出现异常,如网络连接问题或页面不存在等,将捕获requests.RequestException异常,并打印错误信息,同时返回None表示获取页面内容失败。

def get_page_content(self, url):
        """
        发送 HTTP GET 请求获取指定 URL 的页面内容,并设置正确的编码。

        :param url: 要请求的 URL 地址
        :return: 返回获取到的页面文本内容,如果请求失败则返回 None
        """
        try:
            response = requests.get(url, headers=self.headers)
            response.encoding = 'utf8'
            return response.text
        except requests.RequestException as e:
            print(f"请求页面时出错: {e}")
            return None

(三)解析品牌页面(parse_brand_page方法)

   parse_brand_page方法用于解析品牌页面。它接收品牌页面的文本内容作为参数resp。在方法内部,首先使用lxml库的etree模块将页面内容解析为 HTML 元素树。然后,通过特定的 XPath 表达式/html/body/div[2]/div[3]/div/div/div[1]/div/div[2]/ul/li找到页面中品牌列表所在的ul标签下的所有li标签。这里的 XPath 表达式是根据目标网站的页面结构确定的,如果网站页面结构发生变化,可能需要相应调整。

        对于每个品牌的li标签,进一步提取品牌标题和链接。品牌标题通过title = data.xpath("./p/a/@title")[0]获取,链接通过href = data.xpath("./p/a/@href")[0]获取。获取到品牌标题和链接后,调用parse_model_page方法对每个品牌的车型页面进行进一步解析。如果在提取数据过程中出现索引错误,例如 XPath 表达式找不到对应的元素,将打印错误信息提示页面结构可能发生变化。

def parse_brand_page(self, resp):
        """
        解析品牌页面,提取品牌列表中的每个品牌的标题和链接,然后对每个品牌进一步解析。

        :param resp: 品牌页面的文本内容
        """
        html = etree.HTML(resp)
        try:
            # 通过 XPath 找到 ul 标签下的所有 li 标签,这里的 XPath 表达式可能需要根据实际页面结构调整
            data_list = html.xpath('/html/body/div[2]/div[3]/div/div/div[1]/div/div[2]/ul/li')
            for data in data_list:
                title = data.xpath("./p/a/@title")[0]  # 获取品牌标题
                href = data.xpath("./p/a/@href")[0]  # 获取品牌链接
                self.parse_model_page(title, href)
        except IndexError:
            print("在解析品牌页面时,提取数据出现索引错误,可能页面结构发生变化。")

(四)解析车型页面(parse_model_page方法)

   parse_model_page方法用于解析车型页面。它接收品牌标题title和品牌链接href作为参数。在方法内部,首先构建车型页面的 URL,即品牌链接加上chexing.html。然后,调用get_page_content方法获取车型页面的文本内容。如果获取成功,同样使用etree将页面解析为 HTML 元素树。

        通过 XPath 表达式//*[@id="j-model-list"]/li找到车型列表所在的li标签。对于每个车型的li标签,提取车型标题、链接和型号。车型标题通过title = data.xpath("./a/@title")[0]获取,链接通过href = data.xpath("./a/@href")[0]获取,型号通过type_ = data.xpath("./a/text()")[0]获取。获取到车型信息后,调用parse_detail_page方法对车型的详细信息页面进行解析。如果在提取数据过程中出现索引错误,将打印错误信息提示页面结构可能发生变化。

def parse_model_page(self, title, href):
        """
        解析车型页面,提取车型列表中的每个车型的标题、链接和型号,然后对每个车型进一步解析。

        :param title: 品牌标题
        :param href: 品牌链接
        """
        url = href + 'chexing.html'
        response_text = self.get_page_content(url)
        if response_text:
            html = etree.HTML(response_text)
            try:
                # 通过 XPath 找到特定 id 的 ul 标签下的所有 li 标签,这里的 XPath 表达式可能需要根据实际页面结构调整
                data_list = html.xpath('//*[@id="j-model-list"]/li')
                for data in data_list:
                    title = data.xpath("./a/@title")[0]  # 获取车型标题
                    href = data.xpath("./a/@href")[0]  # 获取车型链接
                    type_ = data.xpath("./a/text()")[0]  # 获取车型型号

                    self.parse_detail_page(title, type_, href)
            except IndexError:
                print("在解析车型页面时,提取数据出现索引错误,可能页面结构发生变化。")

(五)解析车型详细页面(parse_detail_page方法)

   parse_detail_page方法用于解析车型详细页面。它接收车型标题title、车型型号type_和车型链接href作为参数。在方法内部,首先调用get_page_content方法获取车型详细页面的文本内容。如果获取成功,使用BeautifulSoup库将页面解析为 BeautifulSoup 对象,以便更方便地提取页面中的表格数据。

        通过find_all('table')方法找到页面中的所有表格标签。对于每个表格,首先提取表格的id属性的后四位并加上字作为一个标识信息kuan,然后遍历表格中的每个td标签。对于每个td标签,提取其中的文本信息。如果td标签中没有img标签,将td标签中的普通文本、span标签中的文本和b标签中的文本进行组合,去除首尾空白字符后添加到td_texts列表中。最后,将kuantd_texts列表作为一个子列表添加到detail列表中,形成车型详细信息的列表结构。

        提取完数据后,将数据插入到 MySQL 数据库中。构建 SQL 插入语句sql = "INSERT INTO 机车 (title, type_, href, detail) VALUES (%s, %s, %s, %s)",并设置插入参数params = (title, type_, href, str(detail)),然后使用数据库游标执行插入操作,并提交事务。如果在提取数据过程中出现异常,将打印错误信息提示页面结构可能发生变化。

def parse_detail_page(self, title, type_, href):
        """
        解析车型详细页面,提取页面中的表格数据,整理成详细信息列表。

        :param title: 车型标题
        :param type_: 车型型号
        :param href: 车型链接
        """
        response_text = self.get_page_content(href)
        if response_text:
            soup = BeautifulSoup(response_text, 'html.parser')

            table_tags = soup.find_all('table')
            detail = []
            try:
                for table in table_tags:
                    kuan = table['id'][-4:] + '款'
                    td_texts = []
                    td_texts.append(kuan)
                    for td in table.find_all('td'):
                        if td.find('img') is None:
                            other_text = td.find(string=True, recursive=False).strip() if td.find(string=True,
                                                                                                  recursive=False) else ""
                            span_text = td.find('span').get_text(strip=True) if td.find('span') else ""
                            if span_text == '价格':
                                span_text = span_text + ':'
                            b_text = td.find('b').get_text(strip=True) if td.find('b') else ""
                            td_texts.append(span_text + other_text + b_text)
                    detail.append(td_texts)
                print(title, type_, href, detail)
                # 将数据插入到 MySQL 数据库
                sql = "INSERT INTO 机车 (title, type_, href, detail) VALUES (%s, %s, %s, %s)"
                params = (title, type_, href, str(detail))
                self.cursor.execute(sql, params)
                self.db.commit()
                # # 将数据存入 MongoDB
                # data_to_insert = {
                #     "title": title,
                #     "type": type_,
                #     "href": href,
                #     "detail": detail
                # }
                # self.mongo_collection.insert_one(data_to_insert)

            except Exception:
                print("在解析车型详细页面时,提取数据出现索引错误,可能页面结构发生变化。")

(六)启动爬虫(run方法)

   run方法是整个爬虫的启动入口。在该方法中,首先调用get_page_content方法获取品牌页面的内容。如果获取成功,即品牌页面内容不为None,则调用parse_brand_page方法开始解析品牌页面,从而启动整个爬虫的流程,后续将依次解析车型页面和车型详细页面,直到完成所有数据的抓取和存储。

def run(self):
        """
        启动整个爬虫流程,先获取品牌页面内容,然后依次进行解析。
        """
        brand_page_content = self.get_page_content(self.url)
        if brand_page_content:
            self.parse_brand_page(brand_page_content)

四、项目总结

全部代码:

# -*- coding:utf-8 -*-
import pymysql
import requests
from bs4 import BeautifulSoup
from lxml import etree


class JiChe(object):
    def __init__(self):
        """
        初始化JiChe类的实例。

        在这里设置了要爬取的基础URL、请求头信息,以及连接到MySQL数据库所需的参数,
        并创建了数据库游标。
        """
        self.url = 'http://www.jiche.com/pinpai/'
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                          "Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.37 "
        }
        # # 连接MongoDB
        # self.mongo_client = MongoClient('localhost', 27017)
        # self.mongo_db = self.mongo_client['your_database_name']
        # self.mongo_collection = self.mongo_db['your_collection_name']
        self.db = pymysql.Connect(
            host='127.0.0.1',
            port=3306,
            user='root',
            password='921108',
            db='fjj'
        )
        self.cursor = self.db.cursor()

    def get_page_content(self, url):
        """
        发送HTTP GET请求获取指定URL的页面内容,并设置正确的编码。

        :param url: 要请求的URL地址
        :return: 返回获取到的页面文本内容,如果请求失败则返回None
        """
        try:
            response = requests.get(url, headers=self.headers)
            response.encoding = 'utf8'
            return response.text
        except requests.RequestException as e:
            print(f"请求页面时出错: {e}")
            return None

    def parse_brand_page(self, resp):
        """
        解析品牌页面,提取品牌列表中的每个品牌的标题和链接,然后对每个品牌进一步解析。

        :param resp: 品牌页面的文本内容
        """
        html = etree.HTML(resp)
        try:
            # 通过XPath找到ul标签下的所有li标签,这里的XPath表达式可能需要根据实际页面结构调整
            data_list = html.xpath('/html/body/div[2]/div[3]/div/div/div[1]/div/div[2]/ul/li')
            for data in data_list:
                title = data.xpath("./p/a/@title")[0]  # 获取品牌标题
                href = data.xpath("./p/a/@href")[0]  # 获取品牌链接
                self.parse_model_page(title, href)
        except IndexError:
            print("在解析品牌页面时,提取数据出现索引错误,可能页面结构发生变化。")

    def parse_model_page(self, title, href):
        """
        解析车型页面,提取车型列表中的每个车型的标题、链接和型号,然后对每个车型进一步解析。

        :param title: 品牌标题
        :param href: 品牌链接
        """
        url = href + 'chexing.html'
        response_text = self.get_page_content(url)
        if response_text:
            html = etree.HTML(response_text)
            try:
                # 通过XPath找到特定id的ul标签下的所有li标签,这里的XPath表达式可能需要根据实际页面结构调整
                data_list = html.xpath('//*[@id="j-model-list"]/li')
                for data in data_list:
                    title = data.xpath("./a/@title")[0]  # 获取车型标题
                    href = data.xpath("./a/@href")[0]  # 获取车型链接
                    type_ = data.xpath("./a/text()")[0]  # 获取车型型号

                    self.parse_detail_page(title, type_, href)
            except IndexError:
                print("在解析车型页面时,提取数据出现索引错误,可能页面结构发生变化。")

    def parse_detail_page(self, title, type_, href):
        """
        解析车型详细页面,提取页面中的表格数据,整理成详细信息列表。

        :param title: 车型标题
        :param type_: 车型型号
        :param href: 车型链接
        """
        response_text = self.get_page_content(href)
        if response_text:
            soup = BeautifulSoup(response_text, 'html.parser')

            table_tags = soup.find_all('table')
            detail = []
            try:
                for table in table_tags:
                    kuan = table['id'][-4:] + '款'
                    td_texts = []
                    td_texts.append(kuan)
                    for td in table.find_all('td'):
                        if td.find('img') is None:
                            other_text = td.find(string=True, recursive=False).strip() if td.find(string=True,
                                                                                                  recursive=False) else ""
                            span_text = td.find('span').get_text(strip=True) if td.find('span') else ""
                            if span_text == '价格':
                                span_text = span_text + ':'
                            b_text = td.find('b').get_text(strip=True) if td.find('b') else ""
                            td_texts.append(span_text + other_text + b_text)
                    detail.append(td_texts)
                print(title, type_, href, detail)
                # 将数据插入到MySQL数据库
                sql = "INSERT INTO 机车 (title, type_, href, detail) VALUES (%s, %s, %s, %s)"
                params = (title, type_, href, str(detail))
                self.cursor.execute(sql, params)
                self.db.commit()
                # # 将数据存入MongoDB
                # data_to_insert = {
                #     "title": title,
                #     "type": type_,
                #     "href": href,
                #     "detail": detail
                # }
                # self.mongo_collection.insert_one(data_to_insert)

            except Exception:
                print("在解析车型详细页面时,提取数据出现索引错误,可能页面结构发生变化。")

    def run(self):
        """
        启动整个爬虫流程,先获取品牌页面内容,然后依次进行解析。
        """
        brand_page_content = self.get_page_content(self.url)
        if brand_page_content:
            self.parse_brand_page(brand_page_content)


if __name__ == '__main__':
    spider = JiChe()
    spider.run()

        通过以上代码实现,我们成功构建了一个机车数据爬虫。它能够从指定的机车网站抓取品牌、车型及详细信息,并存储到 MySQL 数据库中,为后续的数据分析和应用提供了数据基础。然而,在实际应用中,还需要考虑一些问题。例如,网站的页面结构可能会发生变化,这就需要定期检查和调整 XPath 表达式等解析代码,以确保爬虫的稳定性和准确性。同时,为了避免对目标网站造成过大的访问压力,还可以考虑设置合理的爬取间隔时间,遵循网站的 robots.txt 规则等。

        此外,在数据存储方面,虽然本文主要介绍了 MySQL 数据库的使用,但 MongoDB 等非关系型数据库也有其优势,如更好的扩展性和对复杂数据结构的支持。可以根据实际需求进一步优化数据存储方案,或者考虑结合使用多种数据库技术。总之,机车数据爬虫项目具有很大的应用潜力和拓展空间,通过不断地优化和完善,可以为机车行业相关研究和业务提供更强大的数据支持工具。

        希望本文能够帮助读者理解机车数据爬虫的基本原理和实现方法,读者可以根据自己的需求进一步修改和扩展代码,以适应不同的应用场景。

        请注意,在实际使用中,如果涉及到对网站数据的获取,需要确保遵守相关网站的使用条款和法律法规,避免未经授权的访问和数据滥用等问题,本文仅供交流学习,请勿滥用。


http://www.kler.cn/a/420781.html

相关文章:

  • vscode 如何支持点击跳转函数,以C++为例,Python等其它编程语言同理,Visual Studio Code。
  • 防火墙iptables
  • el-table 纵向 横向 多级表头
  • java基础概念46-数据结构1
  • 【C++boost::asio网络编程】有关异步读写api的笔记
  • Linux -初识 与基础指令2
  • 如何让控件始终处于父容器的居中位置(父容器可任意改变大小)
  • vue项目如何设置字体样式font-family,font-family在项目中不生效
  • linux perf安装问题解决
  • Python线程使用
  • linux arm下获取屏幕事件(rk3588)
  • 大模型开发和微调工具Llama-Factory-->训练方法(SFT, RLHF, DPO, KTO)
  • Android 编译和使用libheif
  • playwright 学习复仇记-2 Selector选择器定位元素
  • vmware虚拟机移植
  • 多线程 03 实现方式
  • 三维开发中blender建模后如何完美兼容到threejs
  • SAP HANA 上进行 ABAP 开发:简介
  • 设计模式 更新ing
  • 简单快速的上手python
  • node.js基础学习-zlib模块-压缩解压(八)
  • 护航开源大赛,赋能数字未来
  • Milvus python库 pymilvus 常用操作详解之Collection(上)
  • 算力100问☞第32问:密集计算的关键技术有哪些?
  • Pytest --capture 参数详解:如何控制测试执行过程中的输出行为
  • 【ONE·基础算法 || 动态规划(三)】