Scrapy初窥:爬一个绅士动漫站
最近逛到一个绅士动漫视频站(http://hentaidude.com ),发现它的视频是直接以完整的视频文件方式提供的,试了下可以直接下载,于是我就想试着用爬虫抓取下来。尝试了下使用 python 爬虫框架 scrapy,很容易就撸好了,对方没有做反爬虫处理,直接抓就完事了。
我要抓取的网站是典型的分页显示的网站,涉及列表页和详情页,从列表页获取封面图、文件名等信息以及详情页的链接,再逐个抓取详情页获取具体的视频文件地址,附上 github 链接:qqjt/hdude。
scrapy 安装与项目初始化
- 在 pycharm 里直接创建新项目(hdude),顺便弄好 python3 虚拟环境:
这里我本来想把 ubuntu 里的python3.6 升级到 3.7 的,卸载的时候用了purge
,发现直接把桌面环境整个卸载了,幸好使用apt install gnome
重新装回来了,一波操作差点窒息。 - 初始化 python 虚拟环境的时候出错:
No module named 'distutils.core'
,apt 装个软件包解决:sudo apt install python3-setuptools
。 - 激活虚拟环境:
source venv/bin/activate
。 - 安装scrapy:
pip install scrapy
,出现Python.h: No such file or directory
错误,再装个软件包解决sudo apt install python3-dev
。 - 安装好scrapy 之后初始化项目:
scrapy startproject hdude
,一个 scrapy 项目就自动生成了。
爬虫代码及说明
在 hdude/settings.py
里加了俩条配置,,指定下抓取结果的保存方式:
FEED_URI = u'./animes.csv'
FEED_FORMAT = 'CSV'
在 hdude/items.py
里,指定抓取结果的字段:
from scrapy import Field, Item
class HdudeItem(Item):
name = Field()
url = Field()
like_count = Field()
view_count = Field()
cover_image = Field()
tags = Field()
source = Field()
sources = Field()
新建爬虫 spiders/hdude_spider.py
,里面是抓取的主要处理:
import scrapy
from scrapy.spiders import CrawlSpider
from hdude.items import HdudeItem
import re
class HdudeSpider(CrawlSpider):
name = 'hdude' #爬虫名
start_urls = ['http://hentaidude.com/'] #开始url
def parse(self, response):
self.log('parsing index page: %s' % response.url)
anime_list = response.xpath('//section[@id="content"]/div[@class="videoPost"]')
for anime in anime_list:
item = HdudeItem()
item['name'] = anime.xpath('a[@class="videoLink"]/text()').extract_first()
item['url'] = anime.xpath('a[@class="videoLink"]/@href').extract_first()
item['cover_image'] = anime.xpath('a[@class="thlink"]/div/img/@src').extract_first()
item['like_count'] = anime.xpath('a[@class="heartLink"]/text()').extract_first()
item['view_count'] = anime.xpath('*[@class="thumbViews"]/text()').extract_first()
if item['url']:
# 抓取详情页,并将列表页面获取的 item 信息用 meta 传递过去
yield scrapy.Request(item['url'], callback=self.parse_content, meta={'item': item})
# 继续抓取下个分页
next_page = response.xpath('//div[@class="navigation"]/ul/li/a[contains(text(),"Next >")]/@href')\
.extract_first()
if next_page:
yield scrapy.Request(next_page, callback=self.parse)
def parse_content(self, response):
# 详情页面提取视频地址和标签
self.log('parsing detail page: %s' % response.url)
item = response.meta['item']
sources = re.findall(r'sources\[\'video-source-[0-9]\'\] = \'(.*?)\';',
response.xpath('//body').extract_first())
if sources:
sources = [source for source in sources if source.startswith('http')] # 过滤掉 iframe 视频源
item['source'] = sources[0]
item['sources'] = '|'.join(sources)
tags = response.xpath('//div[@class="new-tags"]/a/text()').extract()
if tags:
item['tags'] = ','.join(tags)
yield item
其中 parse()
方法解析列表页,parse_content()
方法解析详情页。scrapy 自带 xpath 可用来获取页面元素,在获取具体的视频文件地址时,由于播放控件是 js 渲染出来的,需要利用正则表达式从 javascript 代码里提取。yield
是很妙的东东,利用 yield scrapy.Request()
很方便地进行下一步抓取, yield item
就是输出抓取结果,简洁方便,同时利用yield
可以实现异步执行。
抓取及后续处理
在爬虫目录(不是项目根目录)下执行 scrapy crawl hdude
命令执行爬虫,完事后从csv文件内复制出视频地址,放到下载工具里下载就是了,大概九百多个视频(╰_╯)。
附上抓取结果: animes.xlsx