Python 爬虫实战:突破饿了么,抓取美食店铺销量与用户评价
一、引言
二、技术选型
三、分析饿了么页面结构
四、数据爬取
(一)发送请求获取页面内容
(二)提取美食店铺链接
(三)爬取店铺销量与基本信息
(四)爬取用户评价
(五)整合爬取过程
五、数据处理与分析
(一)数据清洗
(二)数据分析
(三)整合数据处理与分析过程
六、总结与注意事项
一、引言
在当今数字化生活中,饿了么作为一款广受欢迎的外卖平台,汇集了大量的美食店铺信息。对于消费者来说,店铺销量和用户评价是选择餐厅和菜品的重要参考依据。而对于餐饮从业者和市场研究者而言,这些数据蕴含着巨大的价值,可以帮助他们了解市场动态、竞争对手情况以及消费者偏好。通过编写 Python 爬虫程序,我们可以从饿了么平台上抓取这些数据,进行深入分析。不过,在进行爬虫操作时,务必遵守相关法律法规和平台规定,不得用于非法目的。本文将详细介绍如何使用 Python 实现这一爬虫项目,包括数据爬取、处理和分析的全过程。
二、技术选型
-
网络请求:
requests
库用于发送 HTTP 请求,获取网页内容。它提供了简洁易用的 API,能够方便地处理各种请求类型,如 GET、POST 等。 -
网页解析:
BeautifulSoup
库用于解析 HTML 和 XML 文档。它可以轻松定位和提取文档中的特定元素,帮助我们从饿了么页面中找到所需的信息。 -
数据存储:
pandas
库用于数据的存储和初步处理。它提供了 DataFrame 数据结构,类似于表格形式,便于对爬取到的数据进行整理、分析和保存为常见的文件格式,如 CSV、Excel 等。 -
正则表达式:
re
模块用于处理正则表达式。在从网页中提取特定格式的数据时,正则表达式能够发挥强大的作用,帮助我们精确匹配和提取符合要求的文本内容。 -
模拟登录(可选):如果需要访问一些需要登录才能查看的数据,可能会用到
selenium
库结合浏览器驱动(如 ChromeDriver)来模拟用户登录操作。
三、分析饿了么页面结构
在编写爬虫之前,我们需要仔细分析饿了么的页面结构。打开饿了么网站或手机应用,进入某个城市的美食页面。以网页版为例,通过浏览器的开发者工具(在 Chrome 浏览器中按 F12 键)来查看页面的 HTML 结构。
我们可以发现,美食店铺的列表通常被包含在特定的 HTML 标签中,每个店铺都有对应的链接指向店铺详情页。在店铺详情页中,销量和用户评价等信息分布在不同的 HTML 元素中。例如,销量可能在某个 <span>
标签内,用户评价可能在专门的评价板块,每个评价也有相应的 HTML 结构来展示评价内容、评分等信息。不同城市、不同页面布局可能会有所差异,所以需要耐心观察和分析,确定要爬取的关键信息所在位置以及它们的 HTML 标签和属性特征。
四、数据爬取
(一)发送请求获取页面内容
import requests
def get_page(url):
headers = {
'User - Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.text
else:
print(f"请求失败,状态码: {response.status_code}")
return None
except requests.RequestException as e:
print(f"请求发生异常: {e}")
return None
代码解释
-
定义函数
get_page
:该函数的作用是发送 HTTP GET 请求到指定的 URL,并返回网页的文本内容。它接受一个参数url
,即要请求的网页地址。 -
设置请求头
headers
:为了模拟真实浏览器的请求行为,避免被网站识别为爬虫而拒绝访问,我们设置了User - Agent
头信息,这里使用了 Chrome 浏览器的常见User - Agent
字符串。 -
发送请求并检查状态码:使用
requests.get
方法发送 GET 请求到目标url
,并将响应结果存储在response
变量中。如果响应状态码为 200,表示请求成功,返回网页的文本内容;否则打印错误信息并返回None
。 -
异常处理:捕获
requests.RequestException
异常,当请求过程中出现网络问题或其他异常时,打印异常信息并返回None
。
(二)提取美食店铺链接
from bs4 import BeautifulSoup
import re
def extract_store_links(html_content):
soup = BeautifulSoup(html_content, 'html.parser')
store_links = []
# 假设店铺链接包含在 class 为 'card - restaurant' 的 a 标签中
links = soup.find_all('a', class_='card - restaurant')
for link in links:
store_link = link['href']
# 确保链接是完整的绝对路径
if not store_link.startswith('https'):
store_link = 'https://www.ele.me' + store_link
store_links.append(store_link)
return store_links
代码解释
-
定义函数
extract_store_links
:该函数用于从给定的网页 HTML 内容中提取所有美食店铺的链接。它接受一个参数html_content
,即网页的文本内容。 -
创建
BeautifulSoup
对象:使用html.parser
解析器将 HTML 内容解析为BeautifulSoup
对象,方便后续操作。 -
查找店铺链接:通过
find_all
方法查找所有class
为card - restaurant
的<a>
标签,这些标签中包含了店铺的链接。 -
处理链接:遍历找到的链接,提取
href
属性值。如果链接不是绝对路径,则将其转换为绝对路径,确保能够正确访问。 -
返回链接列表:将所有处理后的店铺链接存储在
store_links
列表中,并返回该列表。
(三)爬取店铺销量与基本信息
def crawl_store_info(store_url):
html_content = get_page(store_url)
if html_content:
soup = BeautifulSoup(html_content, 'html.parser')
# 提取店铺名称
store_name = soup.find('h1', class_='restaurant - name').text.strip()
# 提取店铺销量
sales = soup.find('span', class_='sales - count').text.strip()
sales = re.sub(r'[\D]', '', sales) # 只保留数字
store_info = {
'店铺名称': store_name,
'销量': sales
}
return store_info
代码解释
-
定义函数
crawl_store_info
:该函数用于爬取单个美食店铺的基本信息和销量。它接受一个参数store_url
,即店铺详情页的链接。 -
获取页面内容:调用
get_page
函数获取店铺详情页的 HTML 内容。 -
解析页面并提取信息:
-
店铺名称:通过查找
class
为restaurant - name
的<h1>
标签,提取店铺的名称,并使用strip
方法去除两端的空白字符。 -
店铺销量:查找
class
为sales - count
的<span>
标签,提取销量文本,然后使用正则表达式re.sub(r'[\D]', '', sales)
去除非数字字符,只保留销量数字。 -
整理店铺信息:将提取到的店铺名称和销量整理成一个字典
store_info
,并返回该字典。
(四)爬取用户评价
def crawl_reviews(store_url):
html_content = get_page(store_url)
if html_content:
soup = BeautifulSoup(html_content, 'html.parser')
reviews = []
review_items = soup.find_all('div', class_='reviews - item')
for item in review_items:
# 提取评价内容
review_content = item.find('div', class_='review - main').text.strip()
# 提取评分
rating = item.find('span', class_='score').text.strip()
review = {
'评价内容': review_content,
'评分': rating
}
reviews.append(review)
return reviews
代码解释
-
定义函数
crawl_reviews
:该函数用于爬取单个美食店铺的用户评价。它接受一个参数store_url
,即店铺详情页的链接。 -
获取页面内容:调用
get_page
函数获取店铺详情页的 HTML 内容。 -
解析页面并提取评价信息:
-
查找评价项:通过
find_all
方法查找所有class
为reviews - item
的<div>
标签,这些标签包含了每个用户评价的信息。 -
提取评价内容:在每个评价项中,查找
class
为review - main
的<div>
标签,提取评价内容,并使用strip
方法去除两端的空白字符。 -
提取评分:查找
class
为score
的<span>
标签,提取评分并去除两端的空白字符。 -
整理评价信息:将提取到的评价内容和评分整理成一个字典
review
,并将所有评价字典添加到reviews
列表中。 -
返回评价列表:返回包含所有用户评价的
reviews
列表。
(五)整合爬取过程
if __name__ == "__main__":
base_url = "https://www.ele.me/restaurant/list/" # 假设这是某个城市美食列表页的基础 URL
page_content = get_page(base_url)
if page_content:
store_links = extract_store_links(page_content)
all_store_info = []
all_reviews = []
for link in store_links:
store_info = crawl_store_info(link)
if store_info:
all_store_info.append(store_info)
reviews = crawl_reviews(link)
if reviews:
all_reviews.extend(reviews)
import pandas as pd
store_df = pd.DataFrame(all_store_info)
review_df = pd.DataFrame(all_reviews)
store_df.to_csv('eleme_store_info.csv', index=False, encoding='utf - 8')
review_df.to_csv('eleme_reviews.csv', index=False, encoding='utf - 8')
代码解释
-
主程序入口:
if __name__ == "__main__":
确保代码在作为主程序运行时执行以下内容。 -
设置基础 URL 并获取页面内容:设置饿了么某个城市美食列表页的基础 URL 为
base_url
,调用get_page
函数获取该页面的 HTML 内容。 -
提取店铺链接:如果成功获取到页面内容,调用
extract_store_links
函数从页面内容中提取所有美食店铺的链接。 -
爬取店铺信息和评价:遍历提取到的店铺链接列表,对于每个链接:
-
调用
crawl_store_info
函数爬取店铺的基本信息和销量,如果成功获取到信息,则将其添加到all_store_info
列表中。 -
调用
crawl_reviews
函数爬取店铺的用户评价,如果成功获取到评价,则将其添加到all_reviews
列表中。 -
数据存储:导入
pandas
库,将all_store_info
列表转换为DataFrame
格式store_df
,将all_reviews
列表转换为DataFrame
格式review_df
。然后分别使用to_csv
方法将店铺信息和用户评价数据保存为 CSV 文件,文件名为eleme_store_info.csv
和eleme_reviews.csv
,不保存索引列,并使用 UTF – 8 编码。
五、数据处理与分析
(一)数据清洗
爬取到的数据可能存在一些不规范或不完整的地方,需要进行清洗。例如,销量数据可能存在格式问题,评价内容可能包含特殊字符等。
import pandas as pd
def clean_data():
store_df = pd.read_csv('eleme_store_info.csv')
review_df = pd.read_csv('eleme_reviews.csv')
# 清洗店铺信息数据
store_df['销量'] = store_df['销量'].astype(int) # 将销量转换为整数类型
# 清洗评价数据
review_df['评价内容'] = review_df['评价内容'].str.replace(r'[^\w\s]', '') # 去除评价内容中的特殊字符
return store_df, review_df
代码解释
-
定义函数
clean_data
:该函数用于对爬取到的店铺信息和用户评价数据进行清洗。 -
读取数据:使用
pandas
的read_csv
方法分别读取之前保存的eleme_store_info.csv
和eleme_reviews.csv
文件,并将数据存储在store_df
和review_df
变量中。 -
清洗店铺信息数据:将
store_df
中的销量
列转换为整数类型,确保数据类型的一致性。 -
清洗评价数据:使用
str.replace
方法去除review_df
中评价内容
列的特殊字符,使评价内容更加规范。 -
返回清洗后的数据:返回清洗后的
store_df
和review_df
。
(二)数据分析
清洗完数据后,我们可以进行一些简单的数据分析。例如,找出销量最高的店铺,分析用户评价的情感倾向等。
def analyze_data(store_df, review_df):
# 找出销量最高的店铺
top_selling_store = store_df.loc[store_df['销量'].idxmax()]
print("销量最高的店铺:", top_selling_store['店铺名称'])
print("销量:", top_selling_store['销量'])
# 分析用户评价的情感倾向(简单示例,可使用更复杂的情感分析方法)
positive_reviews = review_df[review_df['评分'].astype(float) >= 4]
negative_reviews = review_df[review_df['评分'].astype(float) < 4]
print("好评数量:", len(positive_reviews))
print("差评数量:", len(negative_reviews))
代码解释
-
定义函数
analyze_data
:该函数用于对清洗后的店铺信息和用户评价数据进行分析。 -
找出销量最高的店铺:使用
idxmax
方法找到store_df
中销量
列的最大值的索引,然后使用loc
方法获取对应的店铺信息。打印出销量最高的店铺名称和销量。 -
分析用户评价的情感倾向:将
review_df
中的评分
列转换为浮点数类型,然后根据评分大于等于 4 筛选出好评,小于 4 筛选出差评。打印出好评和差评的数量。
(三)整合数据处理与分析过程
if __name__ == "__main__":
store_df, review_df = clean_data()
analyze_data(store_df, review_df)
代码解释
在主程序中,调用 clean_data
函数获取清洗后的数据,然后调用 analyze_data
函数对数据进行分析。
六、总结与注意事项
通过上述步骤,我们成功编写了一个 Python 爬虫程序,从饿了么平台上爬取了美食店铺的销量和用户评价数据,并进行了数据清洗和简单分析。这个项目展示了 Python 爬虫在数据获取和处理方面的强大能力。
然而,在实际应用中,需要注意以下几点:
-
合法性:务必遵守饿了么的使用条款和相关法律法规,不要将爬虫用于商业目的或侵犯平台权益的行为。未经授权的爬取可能会导致法律问题。
-
反爬虫机制:饿了么可能会采取反爬虫措施,如验证码、IP 限制等。如果遇到反爬虫限制,应停止爬取操作,避免对平台造成过大压力。可以考虑使用代理服务器、设置合理的请求间隔等方法来降低被封禁的风险。
-
数据准确性和完整性:由于网页结构可能会发生变化,爬取到的数据可能存在不准确或不完整的情况。需要定期检查和维护爬虫代码,确保数据的质量。
-
道德准则:即使在合法的情况下,也应该遵循道德准则,不要过度爬取数据,以免影响平台的正常运营和其他用户的体验。
希望本文能帮助读者更好地理解和应用 Python 爬虫技术,在合法合规的前提下获取和分析所需的数据。
作者:西攻城狮北