少女祈祷中...

Python爬虫-pixiv关注画师作品[2]


本篇概述:上篇的后续;主要是分析数据接口,拿到关注画师的所有作品的详细信息


1、分析——关注界面

关注界面(默认公开) https://www.pixiv.net/bookmark.php?type=user&rest=show

这里关注画师全在公开界面,虽然非公开获取也是ok的

公开和非公开只是,https://www.pixiv.net/bookmark.php?type=user&rest=show 后面分别是rest=show 和 rest=hide


F12 查看 Elements ,寻找画师列表和页数

  1. 画师列表全在一个 class=members 的 div 中
  2. 画师信息在user-data中,有作者 id、主页 url、作者 name
  1. 最大页数在一个 class=_pager-complex 的 div,倒数第二个li

2、代码——bs4匹配

1
2
3
4
# 找到最大页数
max_num = attention_html_soup.find('div', attrs={'class', '_pager-complex'}).find_all('li')[-2].text
# 画师个人信息
painter_information = attention_html_soup.find('div', attrs={'class', 'members'}).find_all('div', attrs={'class', 'userdata'})

3、代码——获取关注画师的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#获取关注画师界面的信息
def attention_html(self):
# self.return_to = 'https://www.pixiv.net/bookmark.php?type=user&rest=show&p='
# p 是页数
attention_html = self.request(self.return_to)
attention_html_soup = BeautifulSoup(attention_html.text, 'lxml')
#获取最大页数
max_num = attention_html_soup.find('div', attrs={'class', '_pager-complex'}).find_all('li')[-2].text
print('最大页数为%s' % (max_num))
for num in range(1,int(max_num)+1):
attention_html_url = self.return_to + str(num) # 构造每个页面的 url
attention_html = self.request(attention_html_url)
attention_html_soup = BeautifulSoup(attention_html.text,'lxml')
painter_information = attention_html_soup.find('div', attrs={'class', 'members'}).find_all('div', attrs={'class', 'userdata'}) #画师个人信息
for painter in painter_information:
# userdata下的a标签中的data-user_id = 作者id
painter_id = painter.a['data-user_id']
# userdata下的a标签中的data-user_name = 作者name
name = painter.a['data-user_name']
print('{0}:{1}'.format(name,painter_id))
print('已获取所有关注画师的作品信息!!!!')

对于文笔不好的人来说,还是上代码来的舒服

毕竟 talk is cheap, show me code !


4、分析——画师个人主页及数据流向

  1. 这里说下,为什么不获取 user-data 里面的 href 属性?
  2. 正常流程不应该是拿到 href 然后访问 url,获取源码,然后拿到作品信息。
  3. 原因是:这里用的是Ajax请求,所以源码中并没有我们想要的数据
  4. 我们可以直接通过接口拿到这个画师的所有作品的数据。

该画师有73个作品,但是点击进入主页发现只有小小的一部分

勾上 Preserve log,点击查看全部

新加载的页面显示了所有的作品(虽然分为2页)

与之前的 XHR 比较,接下来的目标在红框标出来的三个文件(不一定全是我们的目标)


点击第一个illust,发现 Preview 里是标签 tags (json数据)

点击第二个illust,Preview 里是作品信息 (json数据)

确定了第二个 illust 是目标之后,模仿它进行请求

但是发现他的 url 是一串巨长的字符串,由 一页的所有作品 id 和 is_manga_top=0 拼接而成

url 中作品 id 的拼接顺序是由新到旧(也就是数字大的在前面)

1
url = https://www.pixiv.net/ajax/user/1117751/profile/illusts?ids%5B%5D=73742388&ids%5B%5D=71855085&ids%5B%5D=71849582&ids%5B%5D=71685985&ids%5B%5D=68213121&ids%5B%5D=67765964&ids%5B%5D=67758280&ids%5B%5D=67757922&ids%5B%5D=67619936&ids%5B%5D=67404010&ids%5B%5D=66856979&ids%5B%5D=65998170&ids%5B%5D=65834643&ids%5B%5D=65393332&ids%5B%5D=63861794&ids%5B%5D=63761535&ids%5B%5D=63617575&ids%5B%5D=63475838&ids%5B%5D=63090307&ids%5B%5D=62347061&ids%5B%5D=62200016&ids%5B%5D=62178209&ids%5B%5D=61785521&ids%5B%5D=61656195&ids%5B%5D=61489588&ids%5B%5D=60732958&ids%5B%5D=60588424&ids%5B%5D=60384884&ids%5B%5D=59959669&ids%5B%5D=59706889&ids%5B%5D=59656129&ids%5B%5D=59541311&ids%5B%5D=59180046&ids%5B%5D=58977788&ids%5B%5D=58919004&ids%5B%5D=58029173&ids%5B%5D=57027442&ids%5B%5D=57027347&ids%5B%5D=56527887&ids%5B%5D=56525716&ids%5B%5D=56309403&ids%5B%5D=56110382&ids%5B%5D=55934890&ids%5B%5D=55680445&ids%5B%5D=54917440&ids%5B%5D=54900477&ids%5B%5D=54900429&ids%5B%5D=54900380&is_manga_top=0

下一步目标:既然有所有作品 id 拼接成的 id ,说明是有接口获取所有作品 id 的。


5、模仿请求

在 XHR 中继续寻找,发现一个叫 all 文件,点开 Preview (json数据)

all 的 Request URL: https://www.pixiv.net/ajax/user/1117751/profile/all ,/user/后面的数字是作者的 id

在 Preview 中发现 illusts (插画)属性 ,下面的应该就是作品 id 了,可以自己复制一个去验证一下。

补充:有些画师在 manga (漫画)属性也是有值的,所以这里需要和前面的 illusts 属性合并

Request URL 放到浏览器中去访问,将访问结果复制到 json.cn 进行格式化

6、代码——获取关注画师的所有作品信息

  1. 获得关注画师的信息,比如 id、name
  2. 通过 https://www.pixiv.net/ajax/user/[画师id]/profile/all 来获取画师的所有作品 id
  3. (单图 动图 多图 都在 illusts 属性中,漫画虽然是单图和多图,但在 manga属性 中)
  4. 接着通过构造作品 id 和 is_manga_top=0 的 url 去请求作品的详细信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 获取关注画师界面的信息
def attention_html(self):
# self.return_to = 'https://www.pixiv.net/bookmark.php?type=user&rest=show&p='
# p 是页数
attention_html = self.request(self.return_to)
attention_html_soup = BeautifulSoup(attention_html.text, 'lxml')
# 获取最大页数
max_num = attention_html_soup.find('div', attrs={'class', '_pager-complex'}).find_all('li')[-2].text
print('最大页数为%s' % (max_num))
for num in range(1,int(max_num)+1):
attention_html_url = self.return_to + str(num) # 构造每个页面的 url
attention_html = self.request(attention_html_url)
attention_html_soup = BeautifulSoup(attention_html.text,'lxml')
painter_information = attention_html_soup.find('div', attrs={'class', 'members'}).find_all('div', attrs={'class', 'userdata'}) #画师个人信息
for painter in painter_information:
# userdata下的a标签中的data-user_id = 作者id
painter_id = painter.a['data-user_id']
# userdata下的a标签中的data-user_name = 作者name
name = painter.a['data-user_name']
print('{0}:{1}'.format(name,painter_id))
# 构造url来获取作者的所有作品 id
ajax_url = 'https://www.pixiv.net/ajax/user/{0}/profile/all'.format(painter_id)
self.painter_picture(painter_id,ajax_url,name)
print('获取所有关注画师信息完成!!!!')

# 执行 painter_picture() 应在 attention_html() 的 painter_information 循环中
# self.painter_picture(painter_id,ajax_url,name)
def painter_picture(self,painter_id,ajax_url,name):
ajax_html = self.request(ajax_url)
# 使用json.loads()方法加载进来
ajax_json = json.loads(ajax_html.text)
ajax_illusts = ajax_json["body"]["illusts"]
ajax_manga = ajax_json["body"]["manga"]
# 判断是否有 manga 类型的作品
if len(ajax_manga) == 0:
total_data_dict = dict(ajax_illusts)
else:
# 合并 manga 和 illusts,并转换为字典
total_data_dict = dict(ajax_illusts, **ajax_manga)
# 字典格式:id:None,所以取字典的keys,并转化为list
total_data = list(total_data_dict.keys())
# 这里为什么要排序呢?
# 返回的json里id是从小到大,我们只要[::-1]反转,不就大到小了吗?
# 但是有些画师的作品是有分illusts和manga的,大概就是普通作品(单、多、动)和漫画类型
# 为了防止没拿到漫画类型作品的id,我使用dict(,**)来合并字典
# 这样做是追加到前一个字典的尾部,所以我们必须排序才能正确请求到每一页的48个作品

# 这里用的是冒泡排序(从小到大),刚好学到就用了
for x in range(len_total-1):
for y in range(len_total-1-x):
if total_data[y] > total_data[y+1]:
total_data[y],total_data[y+1] = total_data[y+1],total_data[y]
# 从大到小排序的作品id
total_data = total_data[::-1]
# 按每48个分组,画师每页显示48个作品
limit_num = 48
# after_grouping_list = [[xxx,xxx,xxx],[48个id],[...]...]
after_grouping_list = [total_data[i:i+limit_num] for i in range(0,len(total_data),limit_num)]
print('画师',name,'作品有:',len(after_grouping_list),'页')

# 开始根据作品id来拼接url(就是那个一大串的url)
count = 0 # 每拼接完48个+1
for grouping_list in after_grouping_list:
ids_big = 'https://www.pixiv.net/ajax/user/{}/profile/illusts?'.format(painter_id)
for work_id in grouping_list:
ids = 'ids%5B%5D=' + work_id + '&'
ids_big = ids_big + ids
works_url = ids_big + 'is_manga_top=0'
count += 1
print('第%s页的works_url:%s' % (count,works_url))

# 发起请求获得第count页作品的详细信息
works_html = self.request(works_url)
works_json = json.loads(works_html.text)
works_data = works_json["body"]["works"].values()
for x in works_data:
title = x['title']
folder_id = x['id']
tags = x['tags']
small_url = x['url']
pageCount = x['pageCount']
print('\n作品id:{0}\t作品页数:{1}'.format(folder_id,pageCount))
print('作品标题:{0}'.format(title))
print('作品标签:{0}'.format(tags))
print('作品250*250图片地址:{0}'.format(small_url))

works_data = works_json[“body”][“works”].values()

因为 keys() 是作品 id,values() 里面也有,所以直接 values() 就好了

7、最后

本篇主要是分析数据接口,拿到关注画师的所有作品的详细信息

那么下篇再根据单图动图多图进行图片下载,预计文件存储、其他的小功能和最后的代码汇总得放在下下篇了。

へ( ̄  ̄;へ)

-------------本文结束感谢您的阅读-------------

本文标题:Python爬虫-pixiv关注画师作品[2]

文章作者:Coder-Sakura

发布时间:2019年03月30日 - 18:15:28

最后更新:2019年10月27日 - 15:19:13

原始链接:https://coder-sakura.github.io/blog/2019/03/30/pixiv-two/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。