Beginning Python 3 Crawler Pixiv

Nov 26, 2015


平时逛pixiv,上面有很多很赞的画师,还有很多喜欢的图片。有时就想把这些图片都扒下来保存到本地。那么,这篇博客就来讲讲如何借助Python爬虫来抓图。

pixiv网站更新过后本篇中的登录代码已不能使用,仅供学习参考

在开始之前,先确认电脑中安装了下面两个工具:

在之前两篇博客中已经介绍过部分有关Python自带的urllib的使用和BeautifulSoup的解析方法。接下来,将借助这两个工具来模拟浏览器登录、下载图片。

在现今的pixiv网站中要查看一个画师的作品前,必须先登陆账号。在浏览器中登陆账号会在本地保留Cookie,然后网站通过检测Cookie来判断是否登陆。所以第一步就是使用Python模拟登陆,然后保留Cookie。

打开浏览器登陆pixiv账号,打开审查元素中的Network -> Other,可以看到刚才登录操作浏览器是向login.php接口发送了一个请求(具体请求的URL看General),请求头和请求内容如下图:

login.php

login.php

login.php

下面是Python发送请求模拟登陆的代码:

import urllib, urllib2, cookielib

pixiv_url = 'http://www.pixiv.net/'
member_url = 'http://www.pixiv.net/member_illust.php'
def pixiv_login(my_id, my_pass):
    cookies = cookielib.CookieJar()
    postdata = urllib.urlencode({'pixiv_id':my_id,'pass':my_pass,'mode':'login','skip':'1'})
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies))
    opener.addheaders = [("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36")
                         ,("Referer",'http://www.pixiv.net/'),("Accept","image/webp,image/*,*/*;q=0.8")]
    request  = urllib2.Request(url = 'https://www.secure.pixiv.net/login.php',data = postdata)
    opener.open(request)
    return opener # to get login information(cookies)

那么现在可以通过返回的opener来自由访问pixiv。 接下来来看一个画师的主页,了解一下如何解析网页来找到图片URL下载地址。

打开一个画师的作品列表,看地址栏:

download

地址是通过get请求,参数为页数,画师id来获取网页的。

查看作品列表的HTML代码:

download

列表中包含了各张作品的网址链接,在BS使用中介绍过如何使用CSS筛选器筛选标签,这里不再赘述。

from bs4 import BeautifulSoup
def scan_id(pid):
    global member_url, pixiv_url, opener
    for page_index in range(1, 1001): # 扫面1000页作品
        data = urllib.urlencode({'id':pid, 'type':'all', 'p':page_index})
        request = urllib2.Request(member_url + '?' + data)
        response = opener.open(request)
        soup = BeautifulSoup(response.read())
        if not soup.select('li.image-item'): # 没有这一页的话跳出
            break
        for a in soup.select('li.image-item'):
            get_pixiv_image(pid, pixiv_url + a.a.get('href')) # get all image url and download

接下来看看具体每一张作品的HTML网页代码,找到对应的标签和下载URL。在Pixiv上有单张作品,漫画,动图三种作品类型。

点开一张单张作品的网页

download

有两个地址,一个是当前显示的小图,另一个是大图,大图才是我们要的url地址。

接下来是漫画类型

download

找到超链接再次跳转,然后这个页面里所有img就都是我们要的

download

最后一种是动画,先打开一个动画作品 动画的作品地址比较难找,具体地址如下: 找到一个id=wrapper的div标签,在里面找到一个class=layout-a的div,然后在它上面有一个script脚本。pixiv上的动画就是通过这个脚本一帧帧显示出来的。里面有我们需要的地址。(这个是我个人的解析方法)

download

download

在里面这一堆脚本中可以找到一个src,是一个zip文件,这个就是我们要下载的动图文件

人为的分析完这三种网页代码之后,接下来就是借助BS来解析获取作品的下载地址:

url_queue = Queue.Queue()
def get_pixiv_image(pid, url):
    global url_queue, pixiv_url, opener
    response = opener.open(url)
    soup = BeautifulSoup(response.read())
    if not soup.select('.original-image'):
        if soup.select('._work.multiple'): # 漫画类型
            url = pixiv_url + soup.select('._work.multiple')[0].get('href')
            soup = BeautifulSoup(opener.open(url).read())
            if soup.select('.item-container'):
                for a in soup.select('.item-container'): # 全部下载
                    url_queue.put((pid, a.img.get('data-src')))
        else: # 动画类型
            if soup.select('div.layout-a'):
                script = soup.select('div.layout-a')[0].previous_sibling.string
                (a, b) = (script.find('http:\/\/'), script.find('.zip'))
                if a != -1 and b != -1:
                    url_queue.put((pid, script[a:b+4].replace('\\','')))
    else: # 单张作品
        url_queue.put((pid, soup.select('.original-image')[0].get('data-src')))

对于这种下载程序,一般说来使用多线程更加高效。所以这里使用了一个队列来存储下载链接,以供线程下载。

这里写了一段比较暴力的下载代码

def new_file(pid):
    path = os.getcwd() + '\\' + pid
    if not os.path.exists(path):
        os.makedirs(path)

def download_image():
    global url_queue, isFinish, opener
    while True:
        (pid, url) = ('', '')
        if url_queue.empty(): break
        else:
            (pid, url) = url_queue.get()
            response = opener.open(url)
        if pid and url:
            new_file(pid)
            filename = pid + '/' + url.split('/')[-1]
            if not os.path.exists(filename):
                savefile = open(filename,'wb')
                savefile.write(response.read())
                savefile.close()

结语:这篇post主要是讲如何利用python实现在pixiv上的抓图功能,代码中仍有很多不足可以改进。但是作为我Python入门的小实验,对我的学习也是相当有启发的。有错之处望指正。