python爬取花瓣网任意面板图片

Posted akyna-zh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python爬取花瓣网任意面板图片相关的知识,希望对你有一定的参考价值。

python爬取花瓣网任意面板图片

版本主要使用的库
最初版本re, urllib, requests
升级版本re, urllib, requests, easygui
最终版本re, urllib, easygui, time, selenium

一.最初版本:

  • 优点:不通过json获取, 代码少很简便
  • 缺点:爬到一些多余图片且图片不是最原始的,无法滚动获取所有图片

选择爬取的html代码:

在这里插入图片描述

import re
import urllib.request
import requests


def getHtml(url_):
    page = urllib.request.urlopen(url_)
    html_ = page.read().decode('utf-8')
    obj = open('text.html', 'w', encoding='utf-8')
    obj.write(html_)
    obj.close()
    return html_

if __name__ == '__main__':
	url = input()
    html = getHtml(url)

    # '//hbimg.huabanimg.com/...'
    pattern = re.compile('(//hbimg.huabanimg.com[\\S]+)"')
    keys = re.findall(pattern, html)
    x = 1
    print(len(keys))
    for key in keys:
        url = 'http:' + key
        print('下载图片:' + url)
        obj = open('images/' + str(x) + '.webp', 'wb+')
        image = requests.get(url)
        for data in image:
            obj.write(data)
        obj.close()
        x += 1

二.升级版本:

  • 优点:通过json获取, 图片是最原始的,且不会爬到多余图片
  • 缺点:码量较大,时间较长, 无法滚动获取所有图片

查看面板源码,可以在对应的script中找到面板中图片的json数据:

在app.page[“board”]下可以找到"pins":[{…}],主要图片ID(pin)位于这里面。

在这里插入图片描述
在这里插入图片描述
获取到图片的ID(pin)之后可以对应访问点击图片后进入的地址http://huaban.com/pins/pinId/,并获取页面源码:
在这里插入图片描述显然可见主要图片的源码特征,书写对应正则表达式可以获取图片真实地址。

import urllib.request
import re
import os
import datetime
import easygui
import requests


# 获取网页
# urllib.request可获取完整html 而这里requests.get却无法获取完整代码

def get_html(url):
    # 伪装成浏览器的header
    fake_headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; '
                      'WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
    }
    response = requests.get(url, headers=fake_headers)  # 请求参数里面把假的请求header加上
    html_ = response.content.decode('utf-8')
    return html_


def get_html_1(url):
    page = urllib.request.urlopen(url)
    html_ = page.read().decode('utf-8')
    return html_


# 下载图片
def get_image(path, html):
    # 获取HTML源码里面的"pins": [{...}]部分,主要图片ID位于此部分
    get_app_page_pins = re.compile('"pins":.*', re.S)
    get_str = re.findall(get_app_page_pins, html)[0]

    pin_id = r'"pin_id":(\\d+)'  # 捕获括号内的内容
    pin_id_re = re.compile(pin_id)

    # 获取图片ID,保存在列表中
    id_list = re.findall(pin_id_re, get_str)
    print(len(id_list))
    x = 0
    for pinId in id_list:
        # 获取跳转网页网址
        url_str = r'http://huaban.com/pins/%s/' % pinId

        # 获取点击图片时弹出网页的源码
        pinId_source = get_html_1(url_str)

        # 解析源码,获取原图片的网址
        '''
        <div class="main-image"><div class="image-holder" id="baidu_image_holder">
        <img src="//hbimg.huabanimg.com/64369267b9c8dc7a43da81457658c05b1a752f9329ec0-dSfdfl_fw658/format/webp"
        '''
        img_url_re = re.compile('main-image.*?src="(.*?)"', re.S)
        img_url_list = re.findall(img_url_re, pinId_source)
        img_url = 'http:' + img_url_list[0]
        urllib.request.urlretrieve(img_url, path + '\\%s.jpg' % x)  # urlretrieve()方法直接将远程数据下载到本地
        print("获取成功!%s" % img_url)
        x += 1
    print("保存图片成功!")


# 创建文件夹路径
def createPath():
    while True:
        path = easygui.diropenbox(title='选择你要保存的路径')

        filePath = path + "\\\\" + str(datetime.datetime.now().strftime('%Y-%m-%d %H.%M.%S'))

        isExists = os.path.exists(filePath)
        if not isExists:
            # 创建目录
            os.makedirs(filePath)
            print('%s创建成功!' % filePath)
            break
        else:
            print('%s已存在重新输入!' % filePath)
    return filePath


if __name__ == '__main__':
    url = easygui.enterbox('请输入面板地址:', title='获取花瓣用户任意面板中的图片')
    html = get_html(url)
    obj = open('text.html', 'w', encoding='utf-8')
    obj.write(html)
    obj.close()
    get_image(createPath(), html)  # 调用创建文件夹方法并返回文件夹路径和传入网址

三.最终版本:

发现升级版本无法获取所有图片,为了追求梦想,我们必须找到更好的办法。

自然地认为页面的script代码中的pin会随着页面的滚动加载而变多,可是经过考察发现script代码中的pin并没有发生动态变化。所有该方法作废了。

无法通过json获取pin,那么就只能通过html代码了:

通过滚动页面我们可以发现加载规律

原来的图片对应的代码:
在这里插入图片描述经过滚动,原来的代码逐渐被一些新的代码取代:

在这里插入图片描述
而不难发现他们都有对应的data-id!而data-id就是图片地址中对应的pin

所以我们可以通过webdriver滚动加载页面每滚动一次就进行一次data-id的读取,并利用集合进行去重即可!这样可以获取面板中几乎所有图片的pin(有一小部分图片可能由于网络,地址改变等原因无法获取)

  • 优点:图片是最原始的,且不会爬到多余图片,可以获取面板下所有图片

  • 缺点:码量较大,时间更长

import urllib.request
import urllib.error
import re
import os
import datetime
import easygui
import time
from selenium import webdriver

# 获取网页中所有图片对应的pin
def get_pins(url_):
    options = webdriver.ChromeOptions()
    options.add_argument("User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0")
    driver = webdriver.Chrome(chrome_options=options)
    driver.get(url_)

    js1 = "window.scrollTo(0,document.body.scrollHeight);"
    js2 = "h=document.documentElement.scrollTop; return h"
    pattern = re.compile(r'data-id=?"(\\d*)"')
    origin_top = driver.execute_script(js2)
    pins_ = set()
    # 可以使用大括号 { } 或者 set() 函数创建集合,但是注意如果创建一个空集合必须用 set() 而不是 { },因为{}是用来表示空字典类型的。
    while True:
        html_ = driver.page_source
        pins_1 = re.findall(pattern, html_)
        pins_2 = set(pins_1)
        pins_.update(pins_2)
        driver.execute_script(js1)
        new_top = driver.execute_script(js2)
        time.sleep(1)
        if new_top == origin_top:
            break
        else:
            origin_top = new_top
    driver.close()
    return pins_


# 获取页面html
def get_html_1(url_):
    try:
        page = urllib.request.urlopen(url_)
    except urllib.error.URLError:
        return 'fail'
    html_ = page.read().decode('utf-8')
    return html_


# 下载图片
def get_image(path_, pin_list):
    x = 1
    for pinId in pin_list:
        # 获取跳转网页网址
        url_str = r'http://huaban.com/pins/%s/' % pinId

        # 获取点击图片时弹出网页的源码
        pinId_source = get_html_1(url_str)
        if pinId_source == 'fail':
            continue

        # 解析源码,获取原图片的网址
        '''
        <div class="main-image"><div class="image-holder" id="baidu_image_holder">
        <img src="//hbimg.huabanimg.com/64369267b9c8dc7a43da81457658c05b1a752f9329ec0-dSfdfl_fw658/format/webp"
        '''
        img_url_re = re.compile('main-image.*?src="(.*?)"', re.S)
        img_url_list = re.findall(img_url_re, pinId_source)
        img_url = 'http:' + img_url_list[0]
        try:
            urllib.request.urlretrieve(img_url, path_ + '\\%s.jpg' % x)  # urlretrieve()方法直接将远程数据下载到本地
        except urllib.error.URLError:
            print("获取失败!%s" % img_url)
            continue
        print("获取成功!%s" % img_url)
        x += 1
    print("保存图片成功!")


# 创建文件夹路径
def createPath():
    while True:
        path_ = easygui.diropenbox(title='选择你要保存的路径')
        filePath = path_ + "\\\\" + str(datetime.datetime.now().strftime('%Y-%m-%d %H.%M.%S'))

        isExists = os.path.exists(filePath)
        if not isExists:
            # 创建目录
            os.makedirs(filePath)
            print('%s创建成功!' % filePath)
            break
        else:
            print('%s已存在重新输入!' % filePath)
    return filePath


if __name__ == '__main__':
    url = easygui.enterbox('请输入面板地址:', title='获取花瓣用户任意面板中的图片')
    path = createPath()
    pins = get_pins(url)
    print(len(pins))
    get_image(path, pins)

最终测试:

1.输入网址:
在这里插入图片描述2.选择路径
在这里插入图片描述3.动态加载中…
在这里插入图片描述
4.获取图片中…
在这里插入图片描述5.获取完毕:

在这里插入图片描述
over!!

以上是关于python爬取花瓣网任意面板图片的主要内容,如果未能解决你的问题,请参考以下文章

免费直播 | Node.JS实战:爬虫抓取花瓣网图片饥人谷

周五免费直播 | Node.JS实战:爬虫抓取花瓣网图片饥人谷

perl爬虫01-花瓣网相册

python3爬虫爬取煎蛋网妹纸图片

十一个爆火的Python爬虫实战项目源码不用谢

Python爬虫入门: 蜂鸟网图片爬取之二