可以看到第 14页是动态页面,这里不得不吐槽一下简书,竟然多个接口混用,不应该是 404 not found吗。这样平白给我们的爬取增添了一些麻烦。
不过还好已经知道问题是什么了,这样就只要想出解决办法就好。
观察一下发现当我们在文章栏目下,也就是页数小于 14的时候,文章的标签是激活的,而当我们在动态的栏目下时,动态的标签是激活的(动态两个字下有一个横杠,表示处于激活状态)。
显然在这两个之间同时只能有一个处于激活状态,所以我们可以通过查看文章标签的状态来判断是否爬取完成。
但是... ....
我们又发现在用户的名字下面就有用户的文章数,我们可以获取用户的文章数再计算出总页面数啊!!!(简直被自己蠢哭(;´д`)ゞ)
二、代码实现
分析结束,下面看代码部分:
我们先定义一个生成器,接受简书用户的唯一标识符,先获取用户当前的文章数,然后通过文章数计算出页面数,再根据页面数来生成对应用户的文章列表的链接:
#url生成器
def urlsGenerater(uid):
# 设置请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
}
r = requests.get('https://www.jianshu.com/u/{}?order_by=shared_at&page={}'.format(uid, 1), headers=headers)
dom = etree.HTML(r.text)
#获取文章数量和最大页数
article_num = int(dom.xpath('//div[@class="info"]//li[3]//p/text()')[0].strip())
print(article_num)
max_page_num = article_num / 9
i = 1
while True:
yield 'https://www.jianshu.com/u/{}?order_by=shared_at&page={}'.format(uid, i)
if i >= max_page_num:
break
i+=1
定义一个函数 getArticleItems,接受用户文章列表的链接,返回文章列表的对象数组:
#获取文章的 xpath数组
def getArticleItems(url):
#设置请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
}
# 获取所有 li标签
xpath_items = '//ul[@class="note-list"]/li'
r = requests.get(url, headers=headers)
dom = etree.HTML(r.text)
return dom.xpath(xpath_items)
定义一个函数 getDetails,接受一个文章的 xpath对象,以字典格式返回文章的相关信息:
#获取文章的相关信息
def getDetails(article_item):
# 对每个 li标签再提取
details_xpath = {
'link': './div/a/@href',
'title': './div/a/text()',
'comment_num': './/div[@class="meta"]/a[2]/text()',
'heart_num': './/div[@class="meta"]/span/text()',
}
items = details_xpath.items()
detail = {}
for key, path in items:
detail[key] = ''.join(article_item.xpath(path)).strip()
return detail
将上面的几个模块组合起来,先把获取到的数据打印出来看是否符合要求:
uid = '9bc194fde100'
urls = urlsGenerater(uid)
for url in urls:
article_items = getArticleItems(url)
for article_item in article_items:
print(getDetails(article_item))
打印结果: