经过不断的修改和尝试终于成功的获取到了简书七日热门的数据,主要爬取了以下几个字段:
1.用户
2.标题
3.阅读量
4.评论量
5.获赞量
6.打赏数
7.文章发表时间
8.被哪些专题收录
<strong> 首先我们可以先看一下我们要获取字段的位置 </strong>
<em>框中的其他数据都可以通过xpath获取到,标注箭头的为我在当前页面通过xpath获取不到的或者出现问题的,不过别着急,二级页面中包含我们想要的所有数据。</em>
<em> 从这个页面我们可以看到,在第一级页面中的数据,在这一页都可以看到,但我们只选取我们想要的字段,箭头指向的为上一级页面中通过xpath获取出现问题的字段,在当前页面获取同样出现了问题,所以果断采用正则在源码中获取</em>
<em>我们可以看到在源码中可以看到这些字段,那么就可以通过正则来匹配相应的字段</em>
<strong>我们可以看一下这一篇文章被哪些专题收录,在文章的最下边</strong>
现在我们整明白了所有字段的位置,接下来就开始去抓取这些字段,首先我们先分析页面的分页问题,通过观察发现页面是动态加载的,再点击更多后会请求新的数据,我们来看一下整个页面的url规则,以及获取被收录专题的数据的url规则,我这里简单列一下一些截图,具体可以参考罗罗攀的Python爬虫之简书七日热门数据爬取(异步加载详解),写的特别详细。
<em>通过这些字段我们可以想到一种构造url的方法</em>
def start_requests(self):
for a in range(1,6):
self.url = 'http://www.jianshu.com/trending/weekly?&page=%s' %a
yield scrapy.Request(self.url,self.parse)
我们再来看一下被收录专题的url规则
<em>从这两张图可以看出,URL主要变化点在id和和page,<strong>ID</strong>可以从第三张图片中通过正则从源码获取从最后一张图可以看出收录的专题数有<strong>5页</strong>专题名都存在<strong>title</strong>中所以我们就可以通过第一条URL获取总页数,然后构造其他的url。
构造方法如下:</em>
id = re.findall('{"id":(.*?),', response.text, re.S)[0]
url = 'http://www.jianshu.com/notes/%s/included_collections?page=1' %id
datas = []
result = requests.get(url)
data = json.loads(result.text)
for one in data['collections']:
datas.append(one['title'])
count = data['total_pages']
for one in range(2,count+1):
url = 'http://www.jianshu.com/notes/{}/included_collections?page={}'.format(id,one)
result = requests.get(url)
data = json.loads(result.text)
for one in data['collections']:
datas.append(one['title'])
<strong>通过以上方法可以成功的找到页面和数据,接下来一个很重要的问题,怎们在多级页面中统一数据,因为我们所有的数据是集中在两个页面是实现的,那么怎样才可以实现多级页面传递数据呢?</strong>
完成多级页面传递参数主要是通过<strong>meta</strong>这个属性,具体传递方法可以参照向右奔跑老大的Scrapy抓取在不同级别Request之间传递参数,这篇文章有很详细的介绍。
在这个地方遇到的一个坑:多级页面传递后,发现总是存的是重复的内容,在这里一定要注意,因为肯定是使用for循环去获取相关标题的URL然后进入子页面去获取其他相关字段的信息,但是如果在这个地方定义了item,然后在多级传递参数,就可能出现重复的情况,所以就采用了在第一级页面获取到的数据先用变量存储,通过meta这个属性标签带入二级页面,可以看一下处理代码:
for one in result:
user = one.xpath('div[1]/div/a/text()').extract()[0]
title = one.xpath('a/text()').extract()[0]
zan = one.xpath('div[2]/span[1]/text()').extract()[0]
try:
shang = one.xpath('div[2]/span[2]/text()').extract()[0]
except:
shang = u'0'
publish = one.xpath('div[1]/div/span/@data-shared-at').extract()[0]
link = one.xpath('a/@href').extract()[0]
url = urljoin_rfc(base_url,link)
yield scrapy.Request(url,meta={'user':user,'title':title,
'zan':zan,'shang':shang,'publish':publish},callback=self.parse_type)
<strong>通过上边的简单介绍,应该对整个流程和规则有了一定的了解,下边直接上代码</strong>
1.item.py
import scrapy
class JianshuItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
user = scrapy.Field() #用户
title = scrapy.Field() #标题
count_read = scrapy.Field() #阅读量
ping = scrapy.Field() #评论量
count_zan = scrapy.Field() #喜欢
shang = scrapy.Field() #赞赏
publish = scrapy.Field() #时间
sp_title = scrapy.Field() #被专题书录
2.jianshuSpider.py
#-*- coding:utf-8 -*-
import scrapy
from ..items import JianshuItem
from scrapy.utils.url import urljoin_rfc
import requests
import re
import json
class jianshuSpider(scrapy.Spider):
name = 'jianshu'
def start_requests(self):
for a in range(1,6):
self.url = 'http://www.jianshu.com/trending/weekly?&page=%s' %a
yield scrapy.Request(self.url,self.parse)
def parse(self, response):
result = response.xpath('/html/body/div[1]/div/div[1]/div/ul/li/div')
base_url = 'http://www.jianshu.com'
for one in result:
user = one.xpath('div[1]/div/a/text()').extract()[0]
title = one.xpath('a/text()').extract()[0]
zan = one.xpath('div[2]/span[1]/text()').extract()[0]
try:
shang = one.xpath('div[2]/span[2]/text()').extract()[0]
except:
shang = u'0'
publish = one.xpath('div[1]/div/span/@data-shared-at').extract()[0]
link = one.xpath('a/@href').extract()[0]
url = urljoin_rfc(base_url,link)
yield scrapy.Request(url,meta={'user':user,'title':title,
'zan':zan,'shang':shang,'publish':publish},callback=self.parse_type)
def parse_type(self,response):
item = JianshuItem()
item['user'] = response.meta['user']
item['title'] = response.meta['title']
item['count_read'] = re.findall('"views_count":(.*?),',response.text,re.S)[0]
item['ping'] = re.findall('"comments_count":(.*?),',response.text,re.S)[0]
item['count_zan'] = response.meta['zan']
item['shang'] = response.meta['shang']
item['publish'] = response.meta['publish']
id = re.findall('{"id":(.*?),', response.text, re.S)[0]
url = 'http://www.jianshu.com/notes/%s/included_collections?page=1' %id
datas = []
result = requests.get(url)
data = json.loads(result.text)
for one in data['collections']:
datas.append(one['title'])
count = data['total_pages']
for one in range(2,count+1):
url = 'http://www.jianshu.com/notes/{}/included_collections?page={}'.format(id,one)
result = requests.get(url)
data = json.loads(result.text)
for one in data['collections']:
datas.append(one['title'])
try:
item['sp_title'] = " ".join(datas).encode('utf-8')
except:
item['sp_title'] = u''
yield item
3.结果
总结
先说一下存在的问题:
在对一个页面进行爬取的时候没做一个整体的考虑,所以在不同页面之前传递的参数比较多,代码看起来有点乱,之后可以将所有的数据全部在二级页面获取。
还有一点,可以看到在处理json数据那一块,我选择使用了requests库去操作,这是因为如果使用scrapy.Request去操作的话,要通过回调函数去处理,还要在进行参数的传递,造成很多麻烦,然后准备尝试去写一个用requests和beautifulsoup爬虫,进行一个对比。
<strong>写的第一篇关于思想和技术方面的文章,由于时间的关系所以可能解释的不是很详细,如果存在问题可以在下方评论一起探讨。
最后还是要感谢一下罗罗攀和向右奔跑的相关文章和罗罗攀大哥无私的帮助,如果有时间的话会对数据进一步处理,做一个相关分析。</strong>