结果展示(文末附完整代码):
一、引言
在当今数字化时代,数据对于各个领域的重要性不言而喻。对于机车行业而言,获取丰富的机车品牌、车型及详细信息数据,能够为市场分析、消费者研究等提供有力支持。本文将详细介绍一个使用 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 数据库的操作与讲解。
python
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
表示获取页面内容失败。
python
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 表达式找不到对应的元素,将打印错误信息提示页面结构可能发生变化。
python
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
方法对车型的详细信息页面进行解析。如果在提取数据过程中出现索引错误,将打印错误信息提示页面结构可能发生变化。
python
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
列表中。最后,将kuan
和td_texts
列表作为一个子列表添加到detail
列表中,形成车型详细信息的列表结构。
提取完数据后,将数据插入到 MySQL 数据库中。构建 SQL 插入语句sql = "INSERT INTO 机车 (title, type_, href, detail) VALUES (%s, %s, %s, %s)"
,并设置插入参数params = (title, type_, href, str(detail))
,然后使用数据库游标执行插入操作,并提交事务。如果在提取数据过程中出现异常,将打印错误信息提示页面结构可能发生变化。
python
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
方法开始解析品牌页面,从而启动整个爬虫的流程,后续将依次解析车型页面和车型详细页面,直到完成所有数据的抓取和存储。
python
def run(self):
"""
启动整个爬虫流程,先获取品牌页面内容,然后依次进行解析。
"""
brand_page_content = self.get_page_content(self.url)
if brand_page_content:
self.parse_brand_page(brand_page_content)
四、项目总结
全部代码:
python
# -*- 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 等非关系型数据库也有其优势,如更好的扩展性和对复杂数据结构的支持。可以根据实际需求进一步优化数据存储方案,或者考虑结合使用多种数据库技术。总之,机车数据爬虫项目具有很大的应用潜力和拓展空间,通过不断地优化和完善,可以为机车行业相关研究和业务提供更强大的数据支持工具。
希望本文能够帮助读者理解机车数据爬虫的基本原理和实现方法,读者可以根据自己的需求进一步修改和扩展代码,以适应不同的应用场景。
请注意,在实际使用中,如果涉及到对网站数据的获取,需要确保遵守相关网站的使用条款和法律法规,避免未经授权的访问和数据滥用等问题,本文仅供交流学习,请勿滥用。