python
原创
转载注明出处
代码仅为教学演示
请勿恶意使用
Scrapy是一个功能强大的python爬虫框架,非常适合搭建工程。
Scrapy支持CSS和Xpath的语法,能够很方便地查找选取结点元素,从而提取结构化的数据。
同时,Scrapy支持非同步请求,也支持代理、cookie管理等高阶操作。因此,在上限和性能方面都相较另一个知名的包beautifulsoup更高。
对于一般数据提取的需求,通常只需在Spider类的基础上定义个人的爬虫即可完成目标。
文本主要展示使用scrapy爬取网站评论的过程,想要深入了解scrapy其他功能,可以阅读:官方教程 及相关文档。
命令行安装:
pip install scrapy
目标网站:myanimelist.net
任务:爬取用户的评分和评论
待提取项:用户名,评论提交时间,观看进度,整体评分,小项评分,文本评论
示例页面:灼眼的夏娜评论页
步骤:
- 熟悉scrapy shell
scrapy shell
fetch("https://myanimelist.net/anime/355/Shakugan_no_Shana/reviews")
view(response) # 用默认浏览器打开网页
F12可进入开发者模式,观察网页的大致结构。
善用右键菜单Inspect Element,分析需要提取的数据结构。
image.png image.png
注意到用户的评论数据都在<div>
标签下,可以通过class="borderDark"
做筛选。几个需要提取的项,用户名、上传时间都比较好看出,在<td>
标签下可以提取。一个难点是文本评论信息,网站对长评论进行了缩略,完整的评论需要通过read more展开
image.png
由于这里涉及到child string
的遍历,直接用scrapy
的SelectorList
是比较无力的,考虑使用beautifulsoup
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text)
reviews = soup.find_all("div", class_="borderDark") # 注意不能直接用class
# 查看第一个评论body内的string元素
reviews[0].text
image.png
可以看到前面所要提取的几个数据也在里面,因此在strings
或者stripped_strings
返回的迭代器列表化之后,就能够利用下标统一地提取列表中不同位置的数据。
list(reviews[0].stripped_strings)
image.png
例如时间是第1项,观看进度是第2项,用户名是第5项……依此类推,下面就可以开始建立具体的工程了。
- 工程初始化
# 自行决定工作路径
scrapy startproject MyanimelistSpider
- 创建爬虫脚本
cd MyanimelistSpider
scrapy genspider myanimelist "https://myanimelist.net/anime/355/Shakugan_no_Shana/reviews"
- 定义Item类
# items.py
from scrapy.item import Item,Field
class MyanimelistReview(Item):
username = Field()
submission = Field()
progress = Field()
rating_overall = Field()
rating_story = Field()
rating_animation = Field()
rating_sound = Field()
rating_character = Field()
rating_enjoyment = Field()
helpful = Field()
review = Field()
- 定义Spider类
# spiders/myanimelist.py
import scrapy
from bs4 import BeautifulSoup
from WebSpiders.items import MyanimelistReview
class MyanimelistSpider(scrapy.Spider):
name = "myanimelist"
def start_requests(self):
if not hasattr(self, "url"):
self.url = ""
url = self.url
yield scrapy.Request(url, callback = self.get_review)
def get_review(self, response):
soup = BeautifulSoup(response.text)
reviews = soup.find_all("div", class_="borderDark")
for rev in reviews:
extct = list(rev.stripped_strings)
rating_startat = 12 if rev.find("i") else 11
rating_endat = rating_startat + 11
rating_overall,rating_story,rating_animation,rating_sound,rating_character,rating_enjoyment = extct[rating_startat:rating_endat:2]
review = "\n".join(extct[rating_endat:][:-5])
yield MyanimelistReview(
submission = extct[0],
progress = extct[1],
username = extct[4],
helpful = extct[8],
rating_overall = rating_overall,
rating_story = rating_story,
rating_animation = rating_animation,
rating_sound = rating_sound,
rating_character = rating_character,
rating_enjoyment = rating_enjoyment,
review = review
)
next_page = response.css("div.ml4 a[href*='reviews?p=']")
if len(reviews) != 0:
if next_page:
yield response.follow(next_page[-1], callback=self.get_review)
- 运行爬虫
scrapy crawl myanimelist -o shakugan_no_shana_reviews.json
-
最终结果
由 json 转化而成的 Excel 表格:
image.png
数据到这里就提取完毕了,之后可以根据具体需求做分析。
网友评论