前言
对于那些通过JS来渲染数据的网站,我们要解析出它的html来才能取到想要的数据,通常有两种解决办法:
1、通过selenim调用浏览器(如chrome firefox等)来爬取,将解析的任务交给浏览器。
2、通过splash来解析数据,scrapy可以直接从splash的【空间】中拿到渲染后的数据。
这里介绍scrapy_splash
有个坑
根据它的文档,我们可以知道它依赖于Docker服务,所以你想要使用scrapy_splash就需要先安装docker并跑起来。再根据它的文档进行安装、启动等操作(其实这里有个坑):
$ docker run -p 8050:8050 scrapinghub/splash
项目启动的时候,如果通过这个命令启动,他其实默认给你启动的是http://127.0.0.1:8050/,你可以用浏览器打开来看,看到这个页面就算正常启动了
所以,它的官方示例代码下一句代码(在settings.py中配置):
SPLASH_URL = 'http://192.168.59.103:8050'
你在使用的时候如果按照这句来写,是无法连接splash的,必须写成:
"SPLASH_URL": 'http://127.0.0.1:8050'
其他的配置可以按照文档来写。
代码中的使用
如果使用动态settings配置(避免影响其他爬虫)的话,可以在具体的spider文件中新增:
custom_settings = {
""" 动态settings配置 """
"SPLASH_URL": 'http://127.0.0.1:8050',
"DOWNLOADER_MIDDLEWARES": {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
},
"SPIDER_MIDDLEWARES": {
'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
},
"DUPEFILTER_CLASS": 'scrapy_splash.SplashAwareDupeFilter',
"HTTPCACHE_STORAGE": 'scrapy_splash.SplashAwareFSCacheStorage',
}
name = 'tsinghua'
allowed_domains = ['tv.tsinghua.edu.cn']
start_urls = ['http://tv.tsinghua.edu.cn/publish/video/index.html']
然后再使用的时候,需要将哪个动态页面解析,就要用SplashRequest来发起请求,而不是之前的scrapy.Request来发起,其他的如callback、meta、url都是一样的(如果不一样请以文档为准)下面我放出清华大学视频站的解析代码:
def parse(self, response):
"""
获取导航链接, 自动爬取所有导航url, 交给parseList方法
为了获取js渲染的翻页, 这里用scrapy-splash的SplashRequest来构造请求, 以获得js渲染后的的html数据
"""
totalNav = response.css('#nav li')
for i in totalNav:
urls = i.css('a::attr("href")').extract_first()
yield SplashRequest(url=parse.urljoin(response.url, urls), args={"wait": 1}, callback=self.parseList)
def parseList(self, response):
""" 在列表页获取详情页的url以及视频封面, 传递给parseDetails方法, 最终获取视频和封面等信息 """
totalUrl = response.css('.picnewslist2.clearfix .clearfix ')
for i in totalUrl:
urls = parse.urljoin(response.url, i.css('.contentwraper figcaption a::attr("href")').extract_first())
imagesUrl = parse.urljoin(response.url, i.css('.picwraper img::attr("src")').extract_first())
yield Request(url=urls, meta={"imagesUrl": imagesUrl}, callback=self.parseDetails)
"""
翻页操作
借助scrapy-splash来解析js渲染的html
取出"上一页, 下一页"的页码, 通过re正则来匹配其中的数值
对url进行判断, 是否是第一页。根据url构造不同的下一页nexPageUrl
最后借助scrapy-splash继续解析下一页的html
"""
nextPageList = response.css('a.p::attr("onclick")').extract()
if len(nextPageList) >= 2:
matchRule = re.search('\d+', nextPageList[1])
if matchRule:
nextPageNumber = matchRule.group(0)
thisPageRule = re.search('index_\d+', response.url)
if thisPageRule:
thisPageNumber = thisPageRule.group(0).replace('index_', '')
nexPageUrl = response.url.replace(thisPageNumber, nextPageNumber)
yield SplashRequest(url=nexPageUrl, callback=self.parseList)
else:
nexPageUrlJoin = 'index_' + nextPageNumber
nexPageUrl = response.url.replace('index', nexPageUrlJoin)
yield SplashRequest(url=nexPageUrl, callback=self.parseList)
def parseDetails(self, response):
""" 抽取视频详情, 交给对应item进行序列化 """
imagesUrl =response.meta['imagesUrl']
loaders = tsinghuaVedioItemLoader(item=tsinghuaVedioItem(), response=response)
loaders.add_css("title", "article.article h1::text") # 标题
loaders.add_css("articleContent", '#play_mobile source::attr("src")') # 内容
loaders.add_value("imagesUrl", imagesUrl) # 视频封面地址
loaders.add_value("articleType", 2) # 类型:视频
loaders.add_value("addtime", datetime.now())
loaders.add_value("schoolName", "清华大学")
loaders.add_value("schoolID", 6)
items = loaders.load_item()
yield items