https://travel.qunar.com/travelbook/list.htm?page=1&order=hot_heat 数据采集方法及其实现步骤 1.导包 2.设置请求头 3.发送请求 4.获取html脚本代码 5.解析数据 6.设置爬取一页 7.设置爬取所有页 8.设置多线程 9.把数据保存到csv文件中(注意要设置列标题) 10.爬取每一项数据的详情页信息(如果评论等),并保存到另外一个csv文件中 三、数据清洗过程及其结果 1.缺失值处理 2.重复值 3.异常值与噪声数据处理 3.1 等宽分箱并用均值平滑 3.2 等高分箱并用中位数平滑 3.3 处理噪声数据 3.3.1 使用原则判断噪声数据,并使用众数替换 4.日期格式,没有日期数据,就自己随机插入格式为:2025/5/19的数据,然后通过python代码把日期格式转换为:2025年5月19日 5.数值类型转换(把字符串类型的数值数据转换成数值类型,如int/long等) 6.数据格式化(确保每一项数据中不能包含特殊字符,如:@,#,$等) 7.规范化 7.1把数值数据使用min-max规范发进行转换 7.2 把数值数据使用z-scroe规范化进行转换 7.3 把数值数据使用小数定标规范化进行转换 8.数据保存(把最终处理好的数据保持到新的csv文件中) 9.对爬取的详情页文本信息过滤掉所有特殊符号(如笑脸、@、#、¥等标签图标) 10.对文本内容进行词云图绘制。, 使用scarpy框架对以上信息进行信息采集,数据可视化处理给出的代码能够让大学生看得懂的代码
时间: 2025-07-31 12:59:13 浏览: 6
<think>我们正在使用Scrapy框架来完成Qunar旅行数据的采集、清洗、格式化和可视化。整个过程包括以下几个步骤:1.创建Scrapy项目2.定义要抓取的数据结构(Item)3.编写爬虫(Spider)来抓取数据4.编写数据清洗和格式化(ItemPipeline)5.存储数据(如CSV、数据库等)6.数据可视化(使用matplotlib、seaborn等)注意:由于Qunar网站可能有反爬措施,请确保遵守robots.txt并合理设置下载延迟。下面我们逐步进行:###步骤1:创建Scrapy项目在命令行中执行以下命令:```bashscrapystartprojectqunar_spidercdqunar_spider```###步骤2:定义Item(在items.py中)我们定义要爬取的景点信息,例如景点名称、评分、评论数、地址等。```pythonimportscrapyclassQunarItem(scrapy.Item):#定义字段name=scrapy.Field()#景点名称rating=scrapy.Field()#评分comment_count=scrapy.Field()#评论数address=scrapy.Field()#地址#可以根据需要添加更多字段```###步骤3:编写爬虫(Spider)在spiders目录下创建一个新的爬虫文件,例如`qunar_spider.py`。注意:实际爬取时需要分析Qunar网站的页面结构,这里以景点搜索列表页为例(假设URL结构)。由于网站可能会更新,请根据实际情况调整选择器。```pythonimportscrapyfromqunar_spider.itemsimportQunarItemclassQunarSpider(scrapy.Spider):name="qunar"allowed_domains=["piao.qunar.com"]#以景区票务域名为例start_urls=["https://piao.qunar.com/ticket/list_%E5%8C%97%E4%BA%AC.html"]#北京景点列表defparse(self,response):#提取每个景点的链接attractions=response.css('.sight_item')forattrinattractions:item=QunarItem()item['name']=attr.css('.name::text').get()item['rating']=attr.css('.score::text').get()item['comment_count']=attr.css('.comment::text').re_first(r'(\d+)')#提取数字item['address']=attr.css('.addressspan::text').get()yielditem#分页:如果有下一页,继续爬取next_page=response.css('.next::attr(href)').get()ifnext_page:yieldresponse.follow(next_page,callback=self.parse)```###步骤4:编写Pipeline进行数据清洗和格式化在pipelines.py中,我们可以对爬取到的数据进行清洗和格式化。例如,清洗评分和评论数,将其转换为数字类型,并处理缺失值。```pythonclassQunarSpiderPipeline:defprocess_item(self,item,spider):#清洗评分:如果评分存在,去掉可能的空白字符,转换为浮点数ifitem.get('rating'):item['rating']=float(item['rating'].strip())else:item['rating']=0.0#清洗评论数:提取数字,并转换为整数ifitem.get('comment_count'):#可能包含“条评论”,我们之前用re提取了数字,但如果直接抓取到文本,可以这样处理#这里假设我们之前用re提取了,所以直接转换try:item['comment_count']=int(item['comment_count'])except:item['comment_count']=0else:item['comment_count']=0#清洗地址:去掉地址中的空白字符ifitem.get('address'):item['address']=item['address'].strip()else:item['address']=''returnitem```同时,需要在settings.py中启用该Pipeline:```pythonITEM_PIPELINES={'qunar_spider.pipelines.QunarSpiderPipeline':300,}```###步骤5:存储数据我们可以将数据存储为CSV文件。Scrapy自带了导出为CSV的功能,可以通过在运行爬虫时使用`-o`选项实现:```bashscrapycrawlqunar-oattractions.csv```但如果我们想在Pipeline中存储,也可以使用以下方式(在pipelines.py中添加一个Pipeline类):```pythonimportcsvclassCsvExportPipeline:def__init__(self):self.file=open("qunar_attractions.csv","w",encoding="utf-8",newline='')self.writer=csv.DictWriter(self.file,fieldnames=['name','rating','comment_count','address'])self.writer.writeheader()defprocess_item(self,item,spider):self.writer.writerow(item)returnitemdefclose_spider(self,spider):self.file.close()```然后在settings.py中同时启用这个Pipeline(注意调整优先级,确保在清洗之后)。###步骤6:数据可视化爬虫运行结束后,我们得到一个CSV文件,然后我们可以使用pandas、matplotlib和seaborn进行可视化分析。创建一个新的Python脚本(例如`visualization.py`)进行可视化:```pythonimportpandasaspdimportmatplotlib.pyplotaspltimportseabornassns#读取数据df=pd.read_csv('qunar_attractions.csv')#1.评分分布直方图plt.figure(figsize=(10,6))sns.histplot(df['rating'],bins=20,kde=True)plt.title('DistributionofAttractionRatings')plt.xlabel('Rating')plt.ylabel('Count')plt.savefig('rating_distribution.png')plt.show()#2.评论数与评分的关系plt.figure(figsize=(10,6))sns.scatterplot(x='comment_count',y='rating',data=df)plt.title('RatingvsCommentCount')plt.xlabel('CommentCount')plt.ylabel('Rating')plt.savefig('rating_vs_comment.png')plt.show()#3.按区域(地址)分组统计平均评分(这里以地址中包含的区为例,需要根据实际情况调整)#假设地址中包含区名,我们可以提取区名(这里简化处理,仅取第一个词)#实际中可能需要更复杂的分组df['district']=df['address'].apply(lambdax:x[0:2]iflen(x)>=2elsex)#取前两个字(如“北京”->“北京”,“海淀区”->“海淀”)district_rating=df.groupby('district')['rating'].mean().sort_values(ascending=False)plt.figure(figsize=(12,6))district_rating.plot(kind='bar')plt.title('AverageRatingbyDistrict')plt.xlabel('District')plt.ylabel('AverageRating')plt.savefig('district_rating.png')plt.show()```注意:以上可视化代码需要根据实际数据调整,特别是地址分组部分。###注意事项1.实际爬取时,需要处理Qunar网站的反爬措施,例如设置下载延迟(在settings.py中设置`DOWNLOAD_DELAY`)和User-Agent轮换。2.网站结构可能发生变化,需要根据实际情况调整选择器。3.遵守网站的robots.txt和版权法律。通过以上步骤,我们可以完成从数据采集到可视化的整个流程。这个示例适用于大学生学习Scrapy框架的基本使用。§§相关问题§§1.Scrapy框架中如何设置下载延迟和User-Agent轮换来避免被反爬?2.在Scrapy中如何处理动态加载的网页内容(如JavaScript渲染)?3.除了CSV,Scrapy还支持哪些数据存储方式?如何存储到数据库(如MongoDB)?4.在数据清洗过程中,如何处理缺失值和异常值?5.如何对爬取的数据进行更高级的可视化分析(如热力图、词云等)?</think>以下是使用Scrapy框架完成Qunar旅行数据采集、清洗、格式化和可视化的完整步骤及代码示例,特别针对大学生学习场景设计:
### 一、Scrapy框架完整工作流
```mermaid
graph LR
A[创建Scrapy项目] --> B[定义数据结构]
B --> C[编写爬虫Spider]
C --> D[数据清洗Pipeline]
D --> E[数据存储]
E --> F[数据可视化]
```
### 二、具体步骤及代码
#### 1. 创建Scrapy项目
```bash
# 安装Scrapy
pip install scrapy
# 创建项目
scrapy startproject qunar_crawler
cd qunar_crawler
scrapy genspider attractions piao.qunar.com
```
#### 2. 定义数据结构(items.py)
```python
import scrapy
class QunarItem(scrapy.Item):
name = scrapy.Field() # 景点名称
rating = scrapy.Field() # 评分
comment_count = scrapy.Field() # 评论数
address = scrapy.Field() # 地址
price = scrapy.Field() # 门票价格
open_time = scrapy.Field() # 开放时间
intro = scrapy.Field() # 简介
image_urls = scrapy.Field() # 图片链接
```
#### 3. 编写爬虫(attractions.py)
```python
import scrapy
from qunar_crawler.items import QunarItem
class AttractionsSpider(scrapy.Spider):
name = 'attractions'
start_urls = ['https://piao.qunar.com/ticket/list_%E5%8C%97%E4%BA%AC.html'] # 北京景点示例
def parse(self, response):
for attr in response.css('.sight_item'):
item = QunarItem()
item['name'] = attr.css('.name::text').get()
item['rating'] = attr.css('.score::text').get()
item['comment_count'] = attr.css('.comment::text').re_first(r'(\d+)条')
item['price'] = attr.css('.sight_item_price em::text').get()
item['address'] = attr.css('.address span::text').get()
yield item
# 分页处理
next_page = response.css('.next::attr(href)').get()
if next_page:
yield response.follow(next_page, self.parse)
```
#### 4. 数据清洗(pipelines.py)
```python
import re
from itemadapter import ItemAdapter
class DataCleanPipeline:
def process_item(self, item, spider):
# 价格清洗(提取数字)
if item.get('price'):
item['price'] = float(re.sub(r'[^\d.]', '', item['price']))
# 评论数清洗
if item.get('comment_count'):
item['comment_count'] = int(item['comment_count'])
# 评分处理
if item.get('rating'):
item['rating'] = float(item['rating'])
# 地址标准化
if item.get('address'):
item['address'] = item['address'].replace('地址:', '').strip()
return item
```
#### 5. 数据存储(pipelines.py)
```python
import csv
import json
from datetime import datetime
class JsonWriterPipeline:
def open_spider(self, spider):
self.file = open('qunar_data.json', 'a', encoding='utf-8')
def close_spider(self, spider):
self.file.close()
def process_item(self, item, spider):
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item
class CsvWriterPipeline:
def __init__(self):
self.csv_file = None
self.writer = None
def open_spider(self, spider):
self.csv_file = open('qunar_data.csv', 'w', newline='', encoding='utf-8')
fieldnames = ['name', 'rating', 'comment_count', 'address', 'price']
self.writer = csv.DictWriter(self.csv_file, fieldnames=fieldnames)
self.writer.writeheader()
def close_spider(self, spider):
self.csv_file.close()
def process_item(self, item, spider):
self.writer.writerow({
'name': item.get('name'),
'rating': item.get('rating'),
'comment_count': item.get('comment_count'),
'address': item.get('address'),
'price': item.get('price')
})
return item
```
#### 6. 配置启用(settings.py)
```python
ITEM_PIPELINES = {
'qunar_crawler.pipelines.DataCleanPipeline': 300,
'qunar_crawler.pipelines.JsonWriterPipeline': 400,
'qunar_crawler.pipelines.CsvWriterPipeline': 500,
}
# 避免被反爬
DOWNLOAD_DELAY = 2
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'
ROBOTSTXT_OBEY = False
```
#### 7. 运行爬虫
```bash
scrapy crawl attractions -o output.json
```
### 三、数据可视化(使用Jupyter Notebook)
```python
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 加载数据
df = pd.read_csv('qunar_data.csv')
# 1. 价格分布直方图
plt.figure(figsize=(10,6))
sns.histplot(df['price'], bins=20, kde=True)
plt.title('景点价格分布')
plt.xlabel('价格(元)')
plt.savefig('price_distribution.png')
# 2. 评分与价格关系
plt.figure(figsize=(10,6))
sns.scatterplot(x='rating', y='price', size='comment_count', data=df)
plt.title('评分与价格关系')
plt.savefig('rating_price.png')
# 3. 热门区域分析
df['district'] = df['address'].str.extract(r'(朝阳|海淀|东城|西城|丰台)')
district_counts = df['district'].value_counts()
plt.figure(figsize=(10,6))
district_counts.plot.pie(autopct='%1.1f%%')
plt.title('景点区域分布')
plt.savefig('district_distribution.png')
# 4. 评论数TOP10
top10 = df.nlargest(10, 'comment_count')
plt.figure(figsize=(12,8))
sns.barplot(x='name', y='comment_count', data=top10)
plt.xticks(rotation=45)
plt.title('评论数TOP10景点')
plt.savefig('top10_comments.png')
```
### 四、学习建议
1. **调试技巧**:使用 `scrapy shell 'https://piao.qunar.com'` 测试选择器
2. **增量爬取**:添加 `scrapy-deltafetch` 扩展实现增量采集
3. **反爬应对**:在settings中设置:
```python
DOWNLOAD_DELAY = 3
ROTATING_PROXY_LIST = ['ip1:port', 'ip2:port'] # 使用代理池
```
### 五、常见问题解决
1. **数据缺失**:在Pipeline中添加空值检测
```python
if not item.get('price'):
item['price'] = 0 # 设置默认值
```
2. **动态内容**:使用Splash处理JS渲染
```python
# settings.py
SPLASH_URL = 'http://localhost:8050'
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
}
```
阅读全文