用python爬取P站上的热门插画

心已入冬 提交于 2020-01-30 02:37:39

花了几天学习了网络爬虫这项技术,想要来试试手,于是就写了这第一个实实例。
然后我把目标看向了看的比较多的插画网站pixiv,稍微尝试了一下,其中也碰到了不少困难,还是要多看书,多学点东西。

主要的思路

1. 首先找到我们爬取的目标
也就是获取每日/每周/每月的热门作品排行,主要是插画(其他分区改一下就好)
网址:每日热门的网址
https://www.pixiv.net/ranking.php?mode=daily&content=illust&date=20200128
其中 ‘mode’ 有daily/weekly/monthly三个,风别对应每日,每周,和每月
其中 ‘content=illust’ 就是把分区设为插画
其中 ‘date’ 就是当前日期,可以任意改,但是形如20200101的年月日


2.通过上面这个网址的源代码获得每一幅插画的相关信息
就是从这一堆东西里面提取图片相关信息。(其实有json的文件,但是在我写这部分代码的时候我还没学,所以直接用正则表达式提取出来了)

3. 通过提取的信息保存
我提取出的信息有 排名,作品名,作者名,作品ID
通过提取出来的信息保存为TXT为后面所用

4.下载图片
因为提取出了作品id,那么就可以通过id去做页面访问
如今天的第一作品:https://www.pixiv.net/artworks/79117074
通过改变最后的ID访问到所有其他的插画
同样可以通过查找源代码等方式获得对应图片下载的url

5.最后以二进制保存图片
就用f.write(res.content)就好了

正式开始写

1.初始化
因为这个网站一定要登录才可以访问,所以就找到对应的cookies复制过来就好
然后为了反爬就改下header

# 从浏览器中找到对应的cookies复制到这里
self.cookie = ' *** '
# 初始化header,注意referer
self.header = {
	'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
	'Connection': 'keep-alive',
	'accept': 'application/json, text/javascript, */*; q=0.01',
	'Cookie': self.cookie,
	'referer': ''  # 随着对应需要访问的网址改变
}

2.获取相关信息

    def get_image_url(self):
        # 初始访问地址(通过系统的时间来获取今天的排行)
        daily_url = 'https://www.pixiv.net/ranking.php?mode={}&content=illust&date={}'.format(self.times, self.date)
        # 改变header
        header = self.header.copy()
        header['referer'] = daily_url
        try:
            # 获取网页源代码
            html = requests.get(daily_url, headers=header).text
            # 定义正则规则
            regex = r'section id="(.*?)" class="ranking-item" data-rank="(.*?)" data-rank-text="(.*?)" data-title="(.*?)" data-user-name="(.*?)" data-date="(.*?)" data-view-count="(.*?)" data-rating-count="(.*?)" data-attr="(.*?)" data-id="(.*?)"'
            # 通过正则规则和源代码获取图片信息
            images_data = re.findall(regex, html)

            # 翻页则通过改变p的值,然后从中提取相关信息(p=2,3,4,5...)
            # https://www.pixiv.net/ranking.php?mode=daily&date=20200125&p=2&format=json
        except:
            print('无法获取图片相关信息')
            return False
        # 仅获取作品的 排名,作品名,作者名,作品ID
        else:
            image_data = []
            for img in images_data:
                image = {
                    '排名': img[0],
                    '作品名': img[3],
                    '作者名': img[4],
                    '作品id': img[9]
                }
                image_data.append(image)
                if self.count == int(img[0]):
                    break
            return image_data

3.获取对应下载的地址
其中的重点是 img_url的访问
通过访问img_url,在解析之后就访问判断图片是否有多P,并得到下载地址的列表

    def save_img(self, image_date):
        img_id = image_date['作品id']
        # 因为图片可能多P,所以使用列表储存
        down_list = []
        # 改变header
        header = self.header.copy()
        header["Referer"] = "https://www.pixiv.net/member_illust.php?mode=medium&illust_id={}".format(img_id)
        img_url = "https://www.pixiv.net/touch/ajax/illust/details?illust_id={}".format(img_id)

        try:
            data = requests.get(img_url, header, timeout=15)
        except:
            print("获取{}资源失败".format(img_id))
            return False
        else:
            soup = bs4.BeautifulSoup(data.text, "html.parser")
            try:
            # 划重点(下面这段)
                pre = json.loads(str(soup))["body"]["illust_details"]["manga_a"]  # 列表
                for i in range(len(pre)):
                    down_list.append(pre[i]["url_big"])
            except KeyError:
                pre = json.loads(str(soup))["body"]["illust_details"]["url_big"]  # 字符串
                down_list.append(pre)

            # 将获得分图片下载地址、对用的header(改变referer)以及对用的排名return
            img = down_list, header, image_date['排名']
            return img

4.下载图片

    # 以二进制保存图片(传入地址和图片相关信息)
    def download_the_images(self, img):
        try:
            # 延时
            time.sleep(2)
            for im in range(len(img[0])):
                file_name = img[2] + '_' + img[0][im].split("/")[-1]
                img_url = img[0][im]
                if not os.path.exists(self.img_path):
                    os.mkdir(self.img_path)
                if not os.path.exists(self.img_path + file_name):
                    res = requests.get(img_url, headers=img[1])
                    res.raise_for_status()
                    with open(self.img_path + file_name, 'wb') as f:
                        f.write(res.content)
                    print('{}下载成功'.format(file_name))
        except:
            print('{}下载失败!!!'.format(file_name))

特别感谢

感谢 スイセーヨコ 大佬的代码 【传送门
https://blog.csdn.net/water_blue_story/article/details/98974477

最后加个源代码

# coding: utf-8
import json
import bs4
import requests
import re
import os
import time
from urllib.request import urlretrieve


class download_image:
    def __init__(self):
        # 获取今日排名的日期
        self.date = time.strftime('%Y%m%d', time.localtime(time.time() - 60 * 60 * 24))
        # 保存图片数量
        self.count = 9
        self.times = 'daily'
        self.img_path = ' *自己换个地址* ' + self.times + '_' + str(self.date) + '/'

        # 从浏览器中找到对应的cookies复制到这里
        self.cookie = ' *** '
        # 初始化header,注意referer
        self.header = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
            'Connection': 'keep-alive',
            'accept': 'application/json, text/javascript, */*; q=0.01',
            'Cookie': self.cookie,
            'referer': ''  # 随着对应需要访问的网址改变
        }

    def change_deploy(self):
        y_n = input("是否改变日选周选月选(默认周选):(y/n)")
        if y_n == 'y':
            self.times = input("想按什么选?:(daily/weekly/monthly)")
        y_n = input("是否改变日期(默认今天):(y/n)")
        if y_n == 'y':
            self.date = input("输入指定日期:")
        y_n = input("是否改变获取张数(默认9,最多50):(y/n)")
        if y_n == 'y':
            self.count = input("输入需要获取的张数:")
        y_n = input("是否改变文件储存地址:(y/n)")
        self.img_path = 'I:/5.有趣的东西/image/' + self.times + '_' + str(self.date) + '/'
        if y_n == 'y':
            self.img_path = input("输入指定路径:")
        print(self.img_path)

    # 通过日期获得当日排名图片的相关信息
    def get_image_url(self):
        # 初始访问地址(通过系统的时间来获取今天的排行)
        daily_url = 'https://www.pixiv.net/ranking.php?mode={}&content=illust&date={}'.format(self.times, self.date)
        # 改变header
        header = self.header.copy()
        header['referer'] = daily_url
        try:
            # 获取网页源代码
            html = requests.get(daily_url, headers=header).text
            # 定义正则规则
            regex = r'section id="(.*?)" class="ranking-item" data-rank="(.*?)" data-rank-text="(.*?)" data-title="(.*?)" data-user-name="(.*?)" data-date="(.*?)" data-view-count="(.*?)" data-rating-count="(.*?)" data-attr="(.*?)" data-id="(.*?)"'
            # 通过正则规则和源代码获取图片信息
            images_data = re.findall(regex, html)

            # 翻页则通过改变p的值,然后从中提取相关信息(p=2,3,4,5...)
            # https://www.pixiv.net/ranking.php?mode=daily&date=20200125&p=2&format=json
        except:
            print('无法获取图片相关信息')
            return False
        # 仅获取作品的 排名,作品名,作者名,作品ID
        else:
            image_data = []
            for img in images_data:
                image = {
                    '排名': img[0],
                    '作品名': img[3],
                    '作者名': img[4],
                    '作品id': img[9]
                }
                image_data.append(image)
                if self.count == int(img[0]):
                    break
            return image_data

    def save_img(self, image_date):
        img_id = image_date['作品id']
        # 因为图片可能多P,所以使用列表储存
        down_list = []
        # 改变header
        header = self.header.copy()
        header["Referer"] = "https://www.pixiv.net/member_illust.php?mode=medium&illust_id={}".format(img_id)
        img_url = "https://www.pixiv.net/touch/ajax/illust/details?illust_id={}".format(img_id)

        try:
            data = requests.get(img_url, header, timeout=15)
        except:
            print("获取{}资源失败".format(img_id))
            return False
        else:
            soup = bs4.BeautifulSoup(data.text, "html.parser")
            try:
                pre = json.loads(str(soup))["body"]["illust_details"]["manga_a"]  # 列表
                for i in range(len(pre)):
                    down_list.append(pre[i]["url_big"])
            except KeyError:
                pre = json.loads(str(soup))["body"]["illust_details"]["url_big"]  # 字符串
                down_list.append(pre)

            # 将获得分图片下载地址、对用的header(改变referer)以及对用的排名return
            img = down_list, header, image_date['排名']
            return img

    # 以二进制保存图片(传入地址和图片相关信息)
    def download_the_images(self, img):
        try:
            # 延时
            time.sleep(2)
            for im in range(len(img[0])):
                file_name = img[2] + '_' + img[0][im].split("/")[-1]
                img_url = img[0][im]
                if not os.path.exists(self.img_path):
                    os.mkdir(self.img_path)
                if not os.path.exists(self.img_path + file_name):
                    res = requests.get(img_url, headers=img[1])
                    res.raise_for_status()
                    with open(self.img_path + file_name, 'wb') as f:
                        f.write(res.content)
                    print('{}下载成功'.format(file_name))
        except:
            print('{}下载失败!!!'.format(file_name))

    # 保存本次下载图片的相关信息
    def download_images_information(self, image_data):
        path = self.img_path
        try:
            file_name = path.split("/")[-2] + '.txt'
            if not os.path.exists(self.img_path):
                os.mkdir(self.img_path)
            for imag in image_data:
                with open(self.img_path + file_name, 'a+', encoding='utf-8') as f:
                    line = '排名:{}\n作品名:{}\n作者名:{}\n作品ID:{}\n\n'.format(imag['排名'], imag['作者名'], imag['作品名'], imag['作品id'])
                    f.writelines(line)
        except:
            print('文件保存失败!!!')

    def main(self):
        self.change_deploy()
        # 获得图片信息(排名,作品名,作者名,作品ID)
        image_data = self.get_image_url()
        self.download_images_information(image_data)

        for the_image in range(len(image_data)):
            img = self.save_img(image_data[the_image])
            self.download_the_images(img)


if __name__ == '__main__':
    p = download_image()
    p.main()

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!