前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python爬虫入门:爬取pixiv

Python爬虫入门:爬取pixiv

原创
作者头像
用户7678152
修改2020-08-20 10:03:21
3.7K0
修改2020-08-20 10:03:21
举报
文章被收录于专栏:嵌入式嵌入式嵌入式

终于想开始爬自己想爬的网站了。于是就试着爬P站试试手。

我爬的图的目标网址是: http://www.pixiv.net/search.php?word=%E5%9B%9B%E6%9C%88%E3%81%AF%E5%90%9B%E3%81%AE%E5%98%98,目标是将每一页的图片都爬下来。

一开始以为不用登陆,就直接去爬图片了。

后来发现是需要登录的,但是不会只好去学模拟登陆。

这里是登陆网站 https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index 的headers,

然后还要去获取我们登陆时候需要的data。点住上面的presevelog,找到登陆的网址,点开查看Form Data就可以知道我们post的时候的data需要什么了。这里可以看到有个postkey,多试几次可以发现这个是变化的,即我们要去捕获它,而不能直接输入。

于是退回到登陆界面,F12查看源码,发现有一个postkey,那么我们就可以写一个东西去捕获它,然后把它放到我们post的data里面。

这里给出登陆界面需要的代码:

def __init__(self):
        self.base_url = 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index'
        self.login_url = 'https://accounts.pixiv.net/api/login?lang=zh'
        self.target_url = 'http://www.pixiv.net/search.php?' \
                          'word=%E5%9B%9B%E6%9C%88%E3%81%AF%E5%90%9B%E3%81%AE%E5%98%98&order=date_d&p='
        self.main_url = 'http://www.pixiv.net'
        # headers只要这两个就可以了,之前加了太多其他的反而爬不上
        self.headers = {
            'Referer': 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) '
                          'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
        }
        self.pixiv_id = 'userid'
        self.password = '*****'
        self.post_key = []
        self.return_to = 'http://www.pixiv.net/'
        self.load_path = 'D:\psdcode\Python\pixiv_pic'
        self.ip_list = []

    def login(self):
        post_key_html = se.get(self.base_url, headers=self.headers).text
        post_key_soup = BeautifulSoup(post_key_html, 'lxml')
        self.post_key = post_key_soup.find('input')['value']
        # 上面是去捕获postkey
        data = {
            'pixiv_id': self.pixiv_id,
            'password': self.password,
            'return_to': self.return_to,
            'post_key': self.post_key
        }
        se.post(self.login_url, data=data, headers=self.headers)

愉快地解决完登陆问题之后,就可以开始爬图片啦。

进入target_url:上面的目标网址。

点击目标的位置

点开ul这个标签,发现图片全部都是在<li class="image-item">这里面的,因为我们要爬大一点的图(爬个小图有什么用啊!),所以还要进入一层第一个链接的网址去获取大图,我们可以发现我们只要在main_url((http://www.pixiv.net)),再加上第一个href,就可以跑到图片所在的网址了,于是我们先跳转到图片网址看看怎么提取图片。

发现图片就躺在这里了,而且连标题都有,直接方便了我们存图的名字了。于是我们就可以直接去提取图片了。

注意我们在请求获取图片的时候要加一个referer,否则会403的。referer的找法就和上面一样。

def get_img(self, html, page_num):
        li_soup = BeautifulSoup(html, 'lxml')  # 传入第page_num页的html
        li_list = li_soup.find_all('li', attrs={'class', 'image-item'})   # 找到li所在位置
        # print('get_list succeed')
        # print(li_list)
        for li in li_list:
            href = li.find('a')['href']  # 直接提取第一个href
            # print('get_href succeed')
            # print(href)
            jump_to_url = self.main_url + href  # 跳转到目标的url
            # print('get_jump_to_url succeed')
            jump_to_html = self.get_html(jump_to_url, 3).text  # 获取图片的html
            # print('get_jump_to_html succeed')

            img_soup = BeautifulSoup(jump_to_html, 'lxml')
            img_info = img_soup.find('div', attrs={'class', 'works_display'})\
                .find('div', attrs={'class', '_layout-thumbnail ui-modal-trigger'})
            # 找到目标位置的信息
            if img_info is None:  # 有些找不到url,如果不continue会报错
                continue
            self.download_img(img_info, jump_to_url, page_num)  # 去下载这个图片

    def download_img(self, img_info, href, page_num):
        title = img_info.find('img')['alt']  # 提取标题
        src = img_info.find('img')['src']  # 提取图片位置
        src_headers = self.headers
        src_headers['Referer'] = href  # 增加一个referer,否则会403,referer就像上面登陆一样找
        try:
            html = requests.get(src, headers=src_headers)
            img = html.content
        except:  # 有时候会发生错误导致不能获取图片.直接跳过这张图吧
            print('获取该图片失败')
            return False

接下来轮到下载图片了。这个之前还不怎么会,临时学了一下。

首先是创建文件夹,我这里是每一页就开一个文件夹。

def mkdir(self, path):
        path = path.strip()
        is_exist = os.path.exists(os.path.join(self.load_path, path))
        if not is_exist:
            print('创建一个名字为 ' + path + ' 的文件夹')
            os.makedirs(os.path.join(self.load_path, path))
            os.chdir(os.path.join(self.load_path, path))
            return True
        else:
            print('名字为 ' + path + ' 的文件夹已经存在')
            os.chdir(os.path.join(self.load_path, path))
            return False

def download_img(self, img_info, href, page_num):
        title = img_info.find('img')['alt']  # 提取标题
        src = img_info.find('img')['src']  # 提取图片位置
        src_headers = self.headers
        src_headers['Referer'] = href  # 增加一个referer,否则会403,referer就像上面登陆一样找
        try:
            html = requests.get(src, headers=src_headers)
            img = html.content
        except:  # 有时候会发生错误导致不能获取图片.直接跳过这张图吧
            print('获取该图片失败')
            return False

        title = title.replace('?', '_').replace('/', '_').replace('\\', '_').replace('*', '_').replace('|', '_')\
            .replace('>', '_').replace('<', '_').replace(':', '_').replace('"', '_').strip()
        # 去掉那些不能在文件名里面的.记得加上strip()去掉换行

        if os.path.exists(os.path.join(self.load_path, str(page_num), title + '.jpg')):
            for i in range(1, 100):
                if not os.path.exists(os.path.join(self.load_path, str(page_num), title + str(i) + '.jpg')):
                    title = title + str(i)
                    break
        # 如果重名了,就加上一个数字
        print('正在保存名字为: ' + title + ' 的图片')
        with open(title + '.jpg', 'ab') as f:
            f.write(img)
        print('保存该图片完毕')

这样我们的大体工作就做完了。剩下的是写一个work函数让它开始跑

def work(self):
        self.login()
        for page_num in range(1, 51):  # 太多页了,只跑50页
            path = str(page_num)  # 每一页就开一个文件夹
            self.mkdir(path)  # 创建文件夹
            # print(self.target_url + str(page_num))
            now_html = self.get_html(self.target_url + str(page_num), 3)  # 获取页码
            self.get_img(now_html.text, page_num)  # 获取图片
            print('第 {page} 页保存完毕'.format(page=page_num))
            time.sleep(2)  # 防止太快被反

启动!

大概跑了10页之后,会弹出一大堆信息什么requests不行怎么的。问了下别人应该是被反爬了。

于是去搜了一下资料,http://cuiqingcai.com/3256.html,照着他那样写了使用代理的东西。(基本所有东西都在这学的)。

于是第一个小爬虫就好了。不过代理的东西还没怎么懂,到时候看看,50页爬了两个多钟。

对了。可能网站的源代码会有改动的。因为我吃完饭后用吃饭前的代码继续工作的时候出错了,然后要仔细观察重新干。

# -*- coding:utf-8 -*-
import requests
from bs4 import BeautifulSoup
import os
import time
import re
import random

se = requests.session()


class Pixiv():

    def __init__(self):
        self.base_url = 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index'
        self.login_url = 'https://accounts.pixiv.net/api/login?lang=zh'
        self.target_url = 'http://www.pixiv.net/search.php?' \
                          'word=%E5%9B%9B%E6%9C%88%E3%81%AF%E5%90%9B%E3%81%AE%E5%98%98&order=date_d&p='
        self.main_url = 'http://www.pixiv.net'
        # headers只要这两个就可以了,之前加了太多其他的反而爬不上
        self.headers = {
            'Referer': 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) '
                          'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
        }
        self.pixiv_id = 'userid'
        self.password = '*****'
        self.post_key = []
        self.return_to = 'http://www.pixiv.net/'
        self.load_path = 'D:\psdcode\Python\pixiv_pic'
        self.ip_list = []

    def login(self):
        post_key_html = se.get(self.base_url, headers=self.headers).text
        post_key_soup = BeautifulSoup(post_key_html, 'lxml')
        self.post_key = post_key_soup.find('input')['value']
        # 上面是去捕获postkey
        data = {
            'pixiv_id': self.pixiv_id,
            'password': self.password,
            'return_to': self.return_to,
            'post_key': self.post_key
        }
        se.post(self.login_url, data=data, headers=self.headers)

    def get_proxy(self):
        html = requests.get('http://haoip.cc/tiqu.htm')
        ip_list_temp = re.findall(r'r/>(.*?)<b', html.text, re.S)
        for ip in ip_list_temp:
            i = re.sub('\n', '', ip)
            self.ip_list.append(i.strip())
            print(i.strip())

    ''' 会被反爬,改成使用代理
        def get_tml(self, url):
            response = se.get(url, headers=self.headers)
            return response
    '''
    def get_html(self, url, timeout, proxy=None, num_entries=5):
        if proxy is None:
            try:
                return se.get(url, headers=self.headers, timeout=timeout)
            except:
                if num_entries > 0:
                    print('获取网页出错,5秒后将会重新获取倒数第', num_entries, '次')
                    time.sleep(5)
                    return self.get_html(url, timeout, num_entries = num_entries - 1)
                else:
                    print('开始使用代理')
                    time.sleep(5)
                    ip = ''.join(str(random.choice(self.ip_list))).strip()
                    now_proxy = {'http': ip}
                    return self.get_html(url, timeout, proxy = now_proxy)
        else:
            try:
                return se.get(url, headers=self.headers, proxies=proxy, timeout=timeout)
            except:
                if num_entries > 0:
                    print('正在更换代理,5秒后将会重新获取第', num_entries, '次')
                    time.sleep(5)
                    ip = ''.join(str(random.choice(self.ip_list))).strip()
                    now_proxy = {'http': ip}
                    return self.get_html(url, timeout, proxy = now_proxy, num_entries = num_entries - 1)
                else:
                    print('使用代理失败,取消使用代理')
                    return self.get_html(url, timeout)

    def get_img(self, html, page_num):
        li_soup = BeautifulSoup(html, 'lxml')  # 传入第page_num页的html
        li_list = li_soup.find_all('li', attrs={'class', 'image-item'})   # 找到li所在位置
        # print('get_list succeed')
        # print(li_list)
        for li in li_list:
            href = li.find('a')['href']  # 直接提取第一个href
            # print('get_href succeed')
            # print(href)
            jump_to_url = self.main_url + href  # 跳转到目标的url
            # print('get_jump_to_url succeed')
            jump_to_html = self.get_html(jump_to_url, 3).text  # 获取图片的html
            # print('get_jump_to_html succeed')

            img_soup = BeautifulSoup(jump_to_html, 'lxml')
            img_info = img_soup.find('div', attrs={'class', 'works_display'})\
                .find('div', attrs={'class', '_layout-thumbnail ui-modal-trigger'})
            # 找到目标位置的信息
            if img_info is None:  # 有些找不到url,如果不continue会报错
                continue
            self.download_img(img_info, jump_to_url, page_num)  # 去下载这个图片

    def download_img(self, img_info, href, page_num):
        title = img_info.find('img')['alt']  # 提取标题
        src = img_info.find('img')['src']  # 提取图片位置
        src_headers = self.headers
        src_headers['Referer'] = href  # 增加一个referer,否则会403,referer就像上面登陆一样找
        try:
            html = requests.get(src, headers=src_headers)
            img = html.content
        except:  # 有时候会发生错误导致不能获取图片.直接跳过这张图吧
            print('获取该图片失败')
            return False

        title = title.replace('?', '_').replace('/', '_').replace('\\', '_').replace('*', '_').replace('|', '_')\
            .replace('>', '_').replace('<', '_').replace(':', '_').replace('"', '_').strip()
        # 去掉那些不能在文件名里面的.记得加上strip()去掉换行

        if os.path.exists(os.path.join(self.load_path, str(page_num), title + '.jpg')):
            for i in range(1, 100):
                if not os.path.exists(os.path.join(self.load_path, str(page_num), title + str(i) + '.jpg')):
                    title = title + str(i)
                    break
        # 如果重名了,就加上一个数字
        print('正在保存名字为: ' + title + ' 的图片')
        with open(title + '.jpg', 'ab') as f:  # 图片要用b
            f.write(img)
        print('保存该图片完毕')

    def mkdir(self, path):
        path = path.strip()
        is_exist = os.path.exists(os.path.join(self.load_path, path))
        if not is_exist:
            print('创建一个名字为 ' + path + ' 的文件夹')
            os.makedirs(os.path.join(self.load_path, path))
            os.chdir(os.path.join(self.load_path, path))
            return True
        else:
            print('名字为 ' + path + ' 的文件夹已经存在')
            os.chdir(os.path.join(self.load_path, path))
            return False

    def work(self):
        self.login()
        for page_num in range(1, 51):  # 太多页了,只跑50页
            path = str(page_num)  # 每一页就开一个文件夹
            self.mkdir(path)  # 创建文件夹
            # print(self.target_url + str(page_num))
            now_html = self.get_html(self.target_url + str(page_num), 3)  # 获取页码
            self.get_img(now_html.text, page_num)  # 获取图片
            print('第 {page} 页保存完毕'.format(page=page_num))
            time.sleep(2)  # 防止太快被反


pixiv = Pixiv()
pixiv.work()

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
物联网
腾讯连连是腾讯云物联网全新商业品牌,它涵盖一站式物联网平台 IoT Explorer,连连官方微信小程序和配套的小程序 SDK、插件和开源 App,并整合腾讯云内优势产品能力,如大数据、音视频、AI等。同时,它打通腾讯系 C 端内容资源,如QQ音乐、微信支付、微保、微众银行、医疗健康等生态应用入口。提供覆盖“云-管-边-端”的物联网基础设施,面向“消费物联”和 “产业物联”两大赛道提供全方位的物联网产品和解决方案,助力企业高效实现数字化转型。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档