在PyCharm中创建Scrapy项目并爬取豆瓣电影Top 250数据
以下是从打开PyCharm开始的详细操作步骤:
1. 安装必要的软件
确保你已经安装了以下软件:
- Python (建议版本3.6以上)
- PyCharm
- SQLite (可选,通常Python内置SQLite支持)
2. 打开PyCharm并创建一个新项目
- 打开PyCharm。
- 选择“Create New Project”。
- 在“Location”字段中,输入项目的路径,例如
~/PycharmProjects/douban_scrapy
。 - 确保选中“New environment using”并选择“Virtualenv”。
- 选择Python解释器,建议使用Python 3.6以上版本。
- 点击“Create”创建项目。
3. 安装Scrapy
- 打开PyCharm的终端窗口(View > Tool Windows > Terminal)。
- 在终端中输入以下命令来安装Scrapy:
pip install scrapy
4. 创建Scrapy项目
- 在PyCharm的终端中,导航到项目目录:
cd ~/PycharmProjects/douban_scrapy
- 创建一个新的Scrapy项目:
scrapy startproject douban
- Scrapy会在项目目录下创建一个名为
douban
的文件夹,其中包含Scrapy项目的基本结构。
5. 定义Item
- 打开
douban/douban/items.py
文件。 - 定义需要爬取的数据字段:
import scrapy class MovieItem(scrapy.Item): rank = scrapy.Field() title = scrapy.Field() rating = scrapy.Field() num_reviews = scrapy.Field() quote = scrapy.Field() director = scrapy.Field() actors = scrapy.Field() region = scrapy.Field() genre = scrapy.Field() summary = scrapy.Field()
6. 编写Spider
- 在
douban/douban/spiders
目录下创建一个新的Python文件,命名为douban_spider.py
。 - 编写爬虫代码:
import scrapy from douban.items import MovieItem class DoubanSpider(scrapy.Spider): name = "douban" allowed_domains = ["movie.douban.com"] start_urls = ["https://movie.douban.com/top250"] def parse(self, response): for movie in response.css('div.item'): item = MovieItem() item['rank'] = movie.css('div.pic em::text').get() item['title'] = movie.css('span.title::text').get() item['rating'] = movie.css('span.rating_num::text').get() item['num_reviews'] = movie.css('div.star span:last-child::text').re_first(r'(\d+)') item['quote'] = movie.css('span.inq::text').get() detail_url = movie.css('div.hd a::attr(href)').get() request = scrapy.Request(detail_url, callback=self.parse_details) request.meta['item'] = item yield request next_page = response.css('span.next a::attr(href)').get() if next_page: yield response.follow(next_page, self.parse) def parse_details(self, response): item = response.meta['item'] item['director'] = response.css('a[rel="v:directedBy"]::text').get() item['actors'] = response.css('a[rel="v:starring"]::text').getall() item['region'] = response.xpath('//span[text()="制片国家/地区:"]/following-sibling::text()').get() item['genre'] = response.css('span[property="v:genre"]::text').getall() item['summary'] = response.css('span[property="v:summary"]::text').get().strip() yield item
7. 实现Pipeline
- 打开
douban/douban/pipelines.py
文件。 - 编写数据存储到SQLite的代码:
import sqlite3 class SQLitePipeline: def open_spider(self, spider): self.connection = sqlite3.connect("movies.db") self.cursor = self.connection.cursor() self.cursor.execute(''' CREATE TABLE IF NOT EXISTS top250 ( rank INTEGER PRIMARY KEY, title TEXT, rating REAL, num_reviews INTEGER, quote TEXT, director TEXT, actors TEXT, region TEXT, genre TEXT, summary TEXT ) ''') self.connection.commit() def close_spider(self, spider): self.connection.close() def process_item(self, item, spider): self.cursor.execute(''' INSERT OR REPLACE INTO top250 (rank, title, rating, num_reviews, quote, director, actors, region, genre, summary) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( item['rank'], item['title'], item['rating'], item['num_reviews'], item['quote'], item['director'], ', '.join(item['actors']), item['region'], ', '.join(item['genre']), item['summary'] )) self.connection.commit() return item
8. 配置Pipeline
- 打开
douban/douban/settings.py
文件。 - 添加Pipeline配置:
ITEM_PIPELINES = { 'douban.pipelines.SQLitePipeline': 300, }
9. 运行爬虫
- 打开PyCharm的终端窗口。
- 导航到项目目录:
cd ~/PycharmProjects/douban_scrapy/douban
- 运行爬虫:
scrapy crawl douban
- 确认数据已经被爬取并存储到SQLite数据库中。
10. 数据分析
-
在项目根目录下创建一个新的Python脚本,命名为
data_analysis.py
。 -
编写数据分析脚本:
import sqlite3 import pandas as pd # 连接SQLite数据库 conn = sqlite3.connect('movies.db') # 读取数据 df = pd.read_sql_query("SELECT * FROM top250", conn) # 按制片国家/地区排名前十名的电影 top_regions = df['region'].value_counts().head(10) print("按制片地区排名前十名的电影:") print(top_regions) # 按电影类型排名前十名的电影 top_genres = df['genre'].str.split(', ').explode().value_counts().head(10) print("\n按电影类型排名前十名的电影:") print(top_genres) # 按评论人数排名前十名的电影 top_reviews = df.sort_values(by='num_reviews', ascending=False).head(10) print("\n按评论人数排名前十名的电影:") print(top_reviews[['title', 'num_reviews']])
-
运行数据分析脚本:
python data_analysis.py
11. 提交项目
- 确保所有代码已经完成并保存。
- 提交整个项目文件夹,包括
movies.db
数据库文件。
12. 编写实验报告
- 任务描述。
- 爬虫设计,包括选择和提取页面数据的方法、处理分页和详情页请求的方法、处理数据并存储到SQLite数据库的方法。
- 关键代码说明。
- 爬虫运行结果,包括部分爬取的数据示例。
- 数据分析结果。
- 实现过程中遇到的问题及解决方案。
【其他】 意外情况
修复 Scrapy 运行错误
这个错误通常与 Python 和 Scrapy 版本的兼容性有关。以下是修复此问题的几种方法:
方法 1: 修改 scrapy
配置文件
- 打开
douban/douban/settings.py
文件。 - 添加以下配置来使用
twisted
的reactor
:TWISTED_REACTOR = 'twisted.internet.asyncioreactor.AsyncioSelectorReactor'
方法 2: 升级 twisted
有时,这个问题是由 twisted
的版本引起的。可以尝试升级 twisted
:
- 打开 PyCharm 的终端窗口。
- 运行以下命令来升级
twisted
:pip install --upgrade twisted
方法 3: 使用适配器包
可以安装 scrapy
的适配器包 scrapy-reactor
来解决此问题:
-
打开 PyCharm 的终端窗口。
-
安装适配器包:
pip install scrapy-reactor
-
在
douban/douban/settings.py
文件中添加以下配置:REACTOR = 'scrapy_reactor.asyncioreactor.AsyncioSelectorReactor'
方法 4: 使用不同的 reactor
将 reactor
更改为 twisted.internet.selectreactor.SelectReactor
以避免异步问题:
- 打开
douban/douban/settings.py
文件。 - 添加以下配置来使用
SelectReactor
:from twisted.internet import selectreactor selectreactor.install()
检查其他设置
确保你的项目结构和配置正确无误。以下是配置 scrapy
项目的步骤回顾:
-
项目结构:
douban/ ├── douban/ │ ├── __init__.py │ ├── items.py │ ├── middlewares.py │ ├── pipelines.py │ ├── settings.py │ └── spiders/ │ ├── __init__.py │ └── douban_spider.py ├── scrapy.cfg
-
items.py:
import scrapy class MovieItem(scrapy.Item): rank = scrapy.Field() title = scrapy.Field() rating = scrapy.Field() num_reviews = scrapy.Field() quote = scrapy.Field() director = scrapy.Field() actors = scrapy.Field() region = scrapy.Field() genre = scrapy.Field() summary = scrapy.Field()
-
douban_spider.py:
import scrapy from douban.items import MovieItem class DoubanSpider(scrapy.Spider): name = "douban" allowed_domains = ["movie.douban.com"] start_urls = ["https://movie.douban.com/top250"] def parse(self, response): for movie in response.css('div.item'): item = MovieItem() item['rank'] = movie.css('div.pic em::text').get() item['title'] = movie.css('span.title::text').get() item['rating'] = movie.css('span.rating_num::text').get() item['num_reviews'] = movie.css('div.star span:last-child::text').re_first(r'(\d+)') item['quote'] = movie.css('span.inq::text').get() detail_url = movie.css('div.hd a::attr(href)').get() request = scrapy.Request(detail_url, callback=self.parse_details) request.meta['item'] = item yield request next_page = response.css('span.next a::attr(href)').get() if next_page: yield response.follow(next_page, self.parse) def parse_details(self, response): item = response.meta['item'] item['director'] = response.css('a[rel="v:directedBy"]::text').get() item['actors'] = response.css('a[rel="v:starring"]::text').getall() item['region'] = response.xpath('//span[text()="制片国家/地区:"]/following-sibling::text()').get() item['genre'] = response.css('span[property="v:genre"]::text').getall() item['summary'] = response.css('span[property="v:summary"]::text').get().strip() yield item
-
pipelines.py:
import sqlite3 class SQLitePipeline: def open_spider(self, spider): self.connection = sqlite3.connect("movies.db") self.cursor = self.connection.cursor() self.cursor.execute(''' CREATE TABLE IF NOT EXISTS top250 ( rank INTEGER PRIMARY KEY, title TEXT, rating REAL, num_reviews INTEGER, quote TEXT, director TEXT, actors TEXT, region TEXT, genre TEXT, summary TEXT ) ''') self.connection.commit() def close_spider(self, spider): self.connection.close() def process_item(self, item, spider): self.cursor.execute(''' INSERT OR REPLACE INTO top250 (rank, title, rating, num_reviews, quote, director, actors, region, genre, summary) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( item['rank'], item['title'], item['rating'], item['num_reviews'], item['quote'], item['director'], ', '.join(item['actors']), item['region'], ', '.join(item['genre']), item['summary'] )) self.connection.commit() return item
-
settings.py:
BOT_NAME = 'douban' SPIDER_MODULES = ['douban.spiders'] NEWSPIDER_MODULE = 'douban.spiders' ROBOTSTXT_OBEY = True ITEM_PIPELINES = { 'douban.pipelines.SQLitePipeline': 300, } TWISTED_REACTOR = 'twisted.internet.asyncioreactor.AsyncioSelectorReactor'
通过上述方法,应该能够解决 reactor
的错误问题。如果问题仍然存在,请确保 Scrapy 和 Twisted 版本是最新的,并尝试在一个新的虚拟环境中重新安装这些库。