Python: Scrapy 实践
This commit is contained in:
parent
8551d5d3e8
commit
aa355b3658
@ -1469,3 +1469,323 @@ Scrapy 项目的配置文件(`settings.py`)集中管理了所有与爬虫行
|
|||||||
from .settings_base import *
|
from .settings_base import *
|
||||||
DOWNLOAD_DELAY = 0.5
|
DOWNLOAD_DELAY = 0.5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 实战案例分析
|
||||||
|
|
||||||
|
通过实际项目的分析与实践,可以更好地理解如何将 Scrapy 用于解决现实中的数据抓取问题。本节将通过电商网站的数据采集示例,展示如何进行爬虫设计、数据存储及分析。
|
||||||
|
|
||||||
|
### 电商网站数据采集
|
||||||
|
|
||||||
|
电商网站上通常包含产品名称、价格、库存、评价等关键信息,这些数据对市场分析、竞品研究有重要的参考价值。我们将以模拟电商网站为例,展示如何通过 Scrapy 实现数据采集,并将其存储与分析。
|
||||||
|
|
||||||
|
#### 项目需求与目标
|
||||||
|
|
||||||
|
**需求与目标:**
|
||||||
|
|
||||||
|
- 抓取某电商网站的产品信息,包括产品名称、价格、库存、评价数、产品详情页链接等。
|
||||||
|
- 实现定期抓取,分析产品价格变化趋势,并进行竞品比价。
|
||||||
|
- 需要将数据存储到数据库或导出为 CSV 文件,以便后续数据分析和可视化。
|
||||||
|
|
||||||
|
**具体数据抓取字段:**
|
||||||
|
|
||||||
|
- 产品名称(name)
|
||||||
|
- 价格(price)
|
||||||
|
- 库存(stock)
|
||||||
|
- 评价数(reviews)
|
||||||
|
- 产品详情页链接(url)
|
||||||
|
|
||||||
|
#### **爬虫设计与实现**
|
||||||
|
|
||||||
|
**目标网站:** 我们以模拟的电商网站 http://books.toscrape.com/ 为例,这是一个包含书籍商品信息的示例网站,页面包含产品名称、价格、库存等信息,非常适合做数据抓取示例。
|
||||||
|
|
||||||
|
1. **创建 Scrapy 项目**:首先,我们创建一个 Scrapy 项目,用于抓取数据:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
scrapy startproject ecommerce_spider
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **生成 Spider:**在生成的项目中,使用 `scrapy genspider` 命令来创建一个名为 `book_spider` 的爬虫:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
scrapy genspider book_spider books.toscrape.com
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **编写 Spider**: 创建 Spider 并定义数据抓取逻辑。我们将抓取书籍的名称、价格、库存、评价数等信息。
|
||||||
|
|
||||||
|
```python
|
||||||
|
import scrapy
|
||||||
|
|
||||||
|
class BookSpider(scrapy.Spider):
|
||||||
|
name = 'book_spider'
|
||||||
|
start_urls = ['http://books.toscrape.com/']
|
||||||
|
|
||||||
|
def parse(self, response):
|
||||||
|
# 解析每本书的信息
|
||||||
|
for book in response.css('article.product_pod'):
|
||||||
|
yield {
|
||||||
|
'name': book.css('h3 a::attr(title)').get(),
|
||||||
|
'price': book.css('div.product_price p.price_color::text').get(),
|
||||||
|
'stock': book.css('p.instock.availability::text').re_first('\d+'),
|
||||||
|
'reviews': book.css('p.star-rating::attr(class)').re_first('star-rating (\w+)'),
|
||||||
|
'url': response.urljoin(book.css('h3 a::attr(href)').get()), # 完整的 URL
|
||||||
|
}
|
||||||
|
|
||||||
|
# 处理分页
|
||||||
|
next_page = response.css('li.next a::attr(href)').get()
|
||||||
|
if next_page:
|
||||||
|
yield response.follow(next_page, self.parse)
|
||||||
|
```
|
||||||
|
|
||||||
|
**解释:**
|
||||||
|
|
||||||
|
- 我们通过 CSS 选择器提取需要的信息,如书名、价格、库存情况、星级评测等。
|
||||||
|
- `response.urljoin()` 用于生成书籍详情页的完整链接。
|
||||||
|
- 分页处理:如果存在“下一页”,通过 `response.follow()` 方法继续抓取下一页的内容。
|
||||||
|
|
||||||
|
1. **运行爬虫并保存数据**: 通过以下命令运行爬虫并将数据保存到 CSV 文件中:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
scrapy crawl book_spider -o books.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
爬虫会自动抓取整个网站的书籍数据并保存为 CSV 文件,便于后续的数据分析。
|
||||||
|
|
||||||
|
#### 数据存储与分析
|
||||||
|
|
||||||
|
爬取的数据可以存储到 CSV 文件、数据库(如 MySQL、MongoDB)或其他格式中。这里我们将数据保存到 MySQL 数据库中。
|
||||||
|
|
||||||
|
**将数据存储到 MySQL**:
|
||||||
|
|
||||||
|
1. 安装 MySQL 依赖:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pip install pymysql
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 在 Scrapy 中定义数据管道,将抓取的数据保存到数据库中。
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pymysql
|
||||||
|
import re
|
||||||
|
|
||||||
|
class MySQLPipeline:
|
||||||
|
def open_spider(self, spider):
|
||||||
|
self.connection = pymysql.connect(
|
||||||
|
host='localhost',
|
||||||
|
user='root',
|
||||||
|
password='password', # 修改为您的数据库密码
|
||||||
|
db='ecommerce_db', # 请确保数据库已存在
|
||||||
|
charset='utf8mb4',
|
||||||
|
cursorclass=pymysql.cursors.DictCursor
|
||||||
|
)
|
||||||
|
self.cursor = self.connection.cursor()
|
||||||
|
|
||||||
|
def close_spider(self, spider):
|
||||||
|
self.connection.close()
|
||||||
|
|
||||||
|
def process_item(self, item, spider):
|
||||||
|
# 清洗价格字段,移除货币符号并转换为浮点数
|
||||||
|
if 'price' in item:
|
||||||
|
item['price'] = re.sub(r'[^\d.]', '', item['price']) # 移除非数字字符(如£)
|
||||||
|
item['price'] = float(item['price']) if item['price'] else 0.0
|
||||||
|
|
||||||
|
# 插入到 MySQL 数据库
|
||||||
|
sql = """
|
||||||
|
INSERT INTO products (name, price, stock, reviews, url)
|
||||||
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 为了处理库存的空值,确保 stock 是整数类型
|
||||||
|
stock = item.get('stock')
|
||||||
|
if stock is None:
|
||||||
|
stock = 0
|
||||||
|
|
||||||
|
# 插入数据
|
||||||
|
self.cursor.execute(sql, (
|
||||||
|
item['name'],
|
||||||
|
item['price'],
|
||||||
|
stock,
|
||||||
|
item['reviews'],
|
||||||
|
item['url']
|
||||||
|
))
|
||||||
|
self.connection.commit()
|
||||||
|
return item
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **启用 Pipeline**:
|
||||||
|
|
||||||
|
在 `settings.py` 中启用 MySQL Pipeline:
|
||||||
|
|
||||||
|
```python
|
||||||
|
ITEM_PIPELINES = {
|
||||||
|
'ecommerce_spider.pipelines.MySQLPipeline': 300,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 创建 MySQL 数据库和表。
|
||||||
|
|
||||||
|
```mysql
|
||||||
|
CREATE DATABASE ecommerce_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
USE ecommerce_db;
|
||||||
|
|
||||||
|
CREATE TABLE products (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
price DECIMAL(10, 2) NOT NULL,
|
||||||
|
stock INT DEFAULT 0,
|
||||||
|
reviews VARCHAR(50),
|
||||||
|
url VARCHAR(2083)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **运行爬虫并保存数据**: 通过以下命令运行爬虫并将数据保存到数据库中,爬虫会自动抓取整个网站的书籍数据并保存到数据库,便于后续的数据分析。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
scrapy crawl book_spider
|
||||||
|
```
|
||||||
|
|
||||||
|
6. 接下来,我们使用 Pandas 对数据进行分析,并使用 Matplotlib 进行可视化。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# 安装依赖
|
||||||
|
pip install pandas matplotlib sqlalchemy
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 分析价格变化趋
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
|
# 创建数据库连接
|
||||||
|
engine = create_engine('mysql+pymysql://root:password@localhost/ecommerce_db')
|
||||||
|
|
||||||
|
# 读取数据到 DataFrame
|
||||||
|
df = pd.read_sql('SELECT * FROM products', con=engine)
|
||||||
|
|
||||||
|
# 进行价格分析和可视化
|
||||||
|
print(df['price'].describe())
|
||||||
|
|
||||||
|
# 可视化价格分布
|
||||||
|
df['price'].hist(bins=20)
|
||||||
|
plt.xlabel('Price (£)')
|
||||||
|
plt.ylabel('Number of Books')
|
||||||
|
plt.title('Price Distribution of Books')
|
||||||
|
plt.grid(axis='y')
|
||||||
|
plt.show()
|
||||||
|
```
|
||||||
|
|
||||||
|
通过 Pandas 可以轻松实现数据的统计、清洗和可视化,后续可以进一步进行竞品分析或趋势预测。
|
||||||
|
|
||||||
|
### 社交媒体数据抓取
|
||||||
|
|
||||||
|
社交媒体平台提供了大量实时数据,包括用户发布的动态、评论、点赞等,这些数据可以用于舆情监控、市场分析、广告效果评估等。然而,社交媒体通常会有较强的反爬机制,需要采用特殊的策略。
|
||||||
|
|
||||||
|
#### 反爬策略的应对
|
||||||
|
|
||||||
|
社交媒体网站通常采用如下反爬策略:
|
||||||
|
|
||||||
|
- **频繁的 IP 封禁**:检测过高的请求频率并封禁 IP。
|
||||||
|
- **强制登录**:很多内容只能登录后访问。
|
||||||
|
- **动态加载**:许多数据通过 JavaScript 加载或 AJAX 请求动态更新。
|
||||||
|
|
||||||
|
**应对策略:**
|
||||||
|
|
||||||
|
- **使用代理 IP 池**:通过不断切换 IP 来分散请求。
|
||||||
|
- **模拟登录与会话保持**:使用 Selenium 或 Scrapy 的 `FormRequest` 模拟登录并保持会话。
|
||||||
|
- **处理动态加载**:可以使用 Selenium 或 Splash 处理 JavaScript 渲染。
|
||||||
|
|
||||||
|
#### 实时数据的抓取与更新
|
||||||
|
|
||||||
|
社交媒体数据通常是动态、实时更新的,因此需要定期抓取并更新数据。
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 实时抓取推特上的特定关键词(示例)
|
||||||
|
import scrapy
|
||||||
|
from scrapy_selenium import SeleniumRequest
|
||||||
|
|
||||||
|
class TwitterSpider(scrapy.Spider):
|
||||||
|
name = 'twitter_spider'
|
||||||
|
start_urls = ['https://twitter.com/search?q=python&src=typed_query']
|
||||||
|
|
||||||
|
def start_requests(self):
|
||||||
|
for url in self.start_urls:
|
||||||
|
yield SeleniumRequest(url=url, callback=self.parse)
|
||||||
|
|
||||||
|
def parse(self, response):
|
||||||
|
tweets = response.css('div.css-901oao')
|
||||||
|
for tweet in tweets:
|
||||||
|
yield {
|
||||||
|
'user': tweet.css('span.username::text').get(),
|
||||||
|
'content': tweet.css('div.tweet-text::text').get(),
|
||||||
|
'time': tweet.css('a.tweet-timestamp::attr(title)').get(),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
通过 Scrapy-Selenium 集成,可以处理 Twitter 页面上的 JavaScript 渲染并抓取最新的推文。
|
||||||
|
|
||||||
|
### 新闻资讯聚合爬虫
|
||||||
|
|
||||||
|
新闻聚合爬虫用于从多个新闻源抓取信息,并将不同来源的新闻汇总处理。由于不同网站的结构差异较大,爬虫需要具备处理多源异构数据的能力。
|
||||||
|
|
||||||
|
#### 多源异构数据的处理
|
||||||
|
|
||||||
|
当抓取来自不同新闻源的数据时,页面结构往往各异。因此,我们需要为不同的网站编写相应的解析逻辑,同时保证抓取的数据结构统一。
|
||||||
|
|
||||||
|
```python
|
||||||
|
class NewsSpider1(scrapy.Spider):
|
||||||
|
name = 'news_spider_1'
|
||||||
|
start_urls = ['http://example-news-site1.com']
|
||||||
|
|
||||||
|
def parse(self, response):
|
||||||
|
for article in response.css('div.article'):
|
||||||
|
yield {
|
||||||
|
'title': article.css('h1::text').get(),
|
||||||
|
'content': article.css('div.content::text').get(),
|
||||||
|
'source': 'Site1'
|
||||||
|
}
|
||||||
|
|
||||||
|
class NewsSpider2(scrapy.Spider):
|
||||||
|
name = 'news_spider_2'
|
||||||
|
start_urls = ['http://example-news-site2.com']
|
||||||
|
|
||||||
|
def parse(self, response):
|
||||||
|
for article in response.css('div.news'):
|
||||||
|
yield {
|
||||||
|
'title': article.css('h2::text').get(),
|
||||||
|
'content': article.css('p.summary::text').get(),
|
||||||
|
'source': 'Site2'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
通过编写不同的爬虫来处理不同新闻网站的页面结构,但最终输出的数据格式是统一的。
|
||||||
|
|
||||||
|
#### 数据清洗与内容分析
|
||||||
|
|
||||||
|
抓取到的新闻数据可能包含广告、无关内容或重复信息,因此需要进行数据清洗。可以使用正则表达式、关键词过滤等方法去除无关数据,并通过自然语言处理技术进行内容分析。
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 清洗新闻数据并进行关键词提取
|
||||||
|
import re
|
||||||
|
from collections import Counter
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def clean_text(text):
|
||||||
|
text = re.sub(r'\s+', ' ', text) # 去除多余空白
|
||||||
|
text = re.sub(r'[^\w\s]', '', text) # 去除标点符号
|
||||||
|
return text.lower()
|
||||||
|
|
||||||
|
# 加载数据
|
||||||
|
df = pd.read_csv('news.csv')
|
||||||
|
|
||||||
|
# 清洗数据
|
||||||
|
df['cleaned_content'] = df['content'].apply(clean_text)
|
||||||
|
|
||||||
|
# 提取关键词
|
||||||
|
all_words = ' '.join(df['cleaned_content']).split()
|
||||||
|
word_freq = Counter(all_words)
|
||||||
|
print(word_freq.most_common(10))
|
||||||
|
```
|
||||||
|
|
||||||
|
通过数据清洗和分析,可以提取出新闻中的关键趋势和话题。
|
||||||
|
Loading…
Reference in New Issue
Block a user