Python抓取花瓣网图片脚本

by admin on 2019年9月5日

/**
author: insun
title:Python抓取花瓣网图片脚本
blog:
**/  
    花瓣网的架构:LVS + nginx reverse proxy + NodeJS
cluster,使用MySQL、Redis作为主要的数据存储方案。
是国内类pinterest中图片质量比较高的网站,因为他家早前开的又拍云,没理由做不好图片存储。
  
昨天写了个粗糙的花瓣的抓取程序 网络上的人都抱怨这个有难度 其实是你们没静心下来分析

环境准备

  1. Python 2.7
  2. BeautifulSoup

一:前言

嘀嘀嘀,上车请刷卡。昨天看到了不错的图片分享网——花瓣,里面的图片质量还不错,所以利用selenium+xpath我把它的妹子的栏目下爬取了下来,以图片栏目名称给文件夹命名分类保存到电脑中。这个妹子主页 是动态加载的,如果想获取更多内容可以模拟下拉,这样就可以更多的图片资源。这种之前爬虫中也做过,但是因为网速不够快所以我就抓了19个栏目,一共500多张美图,也已经很满意了。

先看看效果:

图片 1

Paste_Image.png

图片 2

Paste_Image.png

import re

#coding:utf-8
from bs4 import BeautifulSoup
import requests
import urllib

譬如说美女这一栏:
页面是下拉刷新加载页面
加载中的内容应该是ajax请求的,对付ajax请求没有什么好的办法,只有抓取页面的JS,分析JS进行抓取

BeautifulSoup 安装

教程

二:运行环境

  • IDE:Pycharm
  • Python3.6
  • lxml 3.7.2
  • Selenium 3.4.0
  • requests 2.12.4

import ssl

user_agent = ‘Mozilla/5.0 (Windows NT 6.1; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132
Safari/537.36’
headers ={‘User-Agent’:user_agent}

实现代码

import urllib2
import types
from bs4 import BeautifulSoup


def parseHtml(startPageUrl):
    req=urllib2.Request(startPageUrl)
    response=urllib2.urlopen(req)
    html=response.read()

    soup = BeautifulSoup(html)
    tags=soup.findAll('a')

    for tag in tags:
        if isinstance(tag.get('class'),list):
            if 'layer-view' in tag.get('class'):
                lastMax= tag.get('href')[6:-1]
                print "http:"+tag.find('img')['src']
    return lastMax

startPageUrl="https://huaban.com/explore/hua/?j450erym&max=0&limit=20&wfl=1"
lastMax=parseHtml(startPageUrl)
while ((not lastMax) == False):
    print lastMax
    startPageUrl='https://huaban.com/explore/hua/?j450erym&max='+lastMax+'&limit=20&wfl=1'
    lastMax=parseHtml(startPageUrl)

三:实例分析

1.这次爬虫我开始做的思路是:进入这个网页然后来获取所有的图片栏目对应网址,然后进入每一个网页中去获取全部图片。(如下图所示)

图片 3

Paste_Image.png

图片 4

Paste_Image.png

2.但是爬取获取的图片分辨率是236×354,图片质量不够高,但是那个时候已经是晚上1点30之后了,所以第二天做了另一个版本:在这个基础上再进入每个缩略图对应的网页,再抓取像下面这样高清的图片。

图片 5

Paste_Image.png

import urllib.request

 

四:实战代码

1.第一步导入本次爬虫需要的模块

__author__ = '布咯咯_rieuse'
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium import webdriver
import requests
import lxml.html
import os

2.下面是设置webdriver的种类,就是使用什么浏览器进行模拟,可以使用火狐来看它模拟的过程,也可以是无头浏览器PhantomJS来快速获取资源,[‘–load-images=false’,
‘–disk-cache=true’]这个意思是模拟浏览的时候不加载图片和缓存,这样运行速度会加快一些。WebDriverWait标明最大等待浏览器加载为10秒,set_window_size可以设置一下模拟浏览网页的大小。有些网站如果大小不到位,那么一些资源就不加载出来。

# SERVICE_ARGS = ['--load-images=false', '--disk-cache=true']
# browser = webdriver.PhantomJS(service_args=SERVICE_ARGS)
browser = webdriver.Firefox()
wait = WebDriverWait(browser, 10)
browser.set_window_size(1400, 900)

3.parser(url,
param)这个函数用来解析网页,后面有几次都用用到这些代码,所以直接写一个函数会让代码看起来更整洁有序。函数有两个参数:一个是网址,另一个是显性等待代表的部分,这个可以是网页中的某些板块,按钮,图片等等…

def parser(url, param):
    browser.get(url)
    wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, param)))
    html = browser.page_source
    doc = lxml.html.fromstring(html)
    return doc

4.下面的代码就是解析本次主页面 然后获取到每个栏目的网址和栏目的名称,使用xpath来获取栏目的网页时,进入网页开发者模式后,如图所示进行操作。之后需要用栏目名称在电脑中建立文件夹,所以在这个网页中要获取到栏目的名称,这里遇到一个问题,一些名称不符合文件命名规则要剔除,我这里就是一个
* 影响了。

def get_main_url():
    print('打开主页搜寻链接中...')
    try:
        doc = parser('http://huaban.com/boards/favorite/beauty/', '#waterfall')
        name = doc.xpath('//*[@id="waterfall"]/div/a[1]/div[2]/h3/text()')
        u = doc.xpath('//*[@id="waterfall"]/div/a[1]/@href')
        for item, fileName in zip(u, name):
            main_url = 'http://huaban.com' + item
            print('主链接已找到' + main_url)
            if '*' in fileName:
                fileName = fileName.replace('*', '')
            download(main_url, fileName)
    except Exception as e:
        print(e)

图片 6

Paste_Image.png

5.前面已经获取到栏目的网页和栏目的名称,这里就需要对栏目的网页分析,进入栏目网页后,只是一些缩略图,我们不想要这些低分辨率的图片,所以要再进入每个缩略图中,解析网页获取到真正的高清图片网址。这里也有一个地方比较坑人,就是一个栏目中,不同的图片存放dom格式不一样,所以我这样做

img_url = doc.xpath('//*[@id="baidu_image_holder"]/a/img/@src')
img_url2 = doc.xpath('//*[@id="baidu_image_holder"]/img/@src')

这就把两种dom格式中的图片地址都获取了,然后把两个地址list合并一下。img_url +=img_url2
在本地创建文件夹使用filename = 'image\{}\'.format(fileName) + str(i) + '.jpg'表示文件保存在与这个爬虫代码同级目录image下,然后获取的图片保存在image中按照之前获取的栏目名称的文件夹中。

def download(main_url, fileName):
    print('-------准备下载中-------')
    try:
        doc = parser(main_url, '#waterfall')
        if not os.path.exists('image\\' + fileName):
            print('创建文件夹...')
            os.makedirs('image\\' + fileName)
        link = doc.xpath('//*[@id="waterfall"]/div/a/@href')
        # print(link)
        i = 0
        for item in link:
            i += 1
            minor_url = 'http://huaban.com' + item
            doc = parser(minor_url, '#pin_view_page')
            img_url = doc.xpath('//*[@id="baidu_image_holder"]/a/img/@src')
            img_url2 = doc.xpath('//*[@id="baidu_image_holder"]/img/@src')
            img_url +=img_url2
            try:
                url = 'http:' + str(img_url[0])
                print('正在下载第' + str(i) + '张图片,地址:' + url)
                r = requests.get(url)
                filename = 'image\\{}\\'.format(fileName) + str(i) + '.jpg'
                with open(filename, 'wb') as fo:
                    fo.write(r.content)
            except Exception:
                print('出错了!')
    except Exception:
        print('出错啦!')


if __name__ == '__main__':
    get_main_url()

def getHtml(url):

def Schedule(blocknum,blocksize,totalsize):

图片 7

五:总结

这次爬虫继续练习了Selenium和xpath的使用,在网页分析的时候也遇到很多问题,只有不断练习才能把自己不会部分减少,当然这次爬取了500多张妹纸还是挺养眼的。

学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入学习交流群
626062078,我们一起学Python!

  page = urllib.request.urlopen(url)

#blocknum:已经下载的数据块
#blocksize:数据块的大小
#totalsize:远程文件的大小

正确的请求就返回一串Json
{“filter”:”pin:category:beauty”,
“pins”:[{“pin_id”:46189172,”user_id”:755324,”board_id”:2425342,”file_id”:11427806,”file”:{“farm”:”farm1″,”bucket”:”hbimg”,”key”:”3d971b295b79e397765a0dde013cf574b22166848924-mlLarI”,”type”:”image/jpeg”,”width”:354,”height”:440,”frames”:1},”media_type”:0,”source”:null,”link”:null,”raw_text”:”春季造型攻略
#小清新#”,”text_meta”:{“tags”:[{“start”:7,”offset”:5}]},”via”:1,”via_user_id”:0,”original”:null,”created_at”:”1361176634″,”like_count”:0,”comment_count”:0,”repin_count”:0,”is_private”:0,”orig_source”:null,
“user”:{“user_id”:755324,”username”:”爱造型”,”urlname”:”aizaoxing”,”created_at”:”1346810536″,
“avatar”:{“id”:5664391,”farm”:”farm1″,”bucket”:”hbimg”,”key”:”97f2e2cbdee392f3f7642ec8bd12447f673f73221007-yThjgP”,”type”:”image/jpeg”,”width”:100,”height”:100,”frames”:1}},”board”:{“board_id”:2425342,”user_id”:755324,”title”:”爱造型美丽分享#美丽造型#”,”description”:””,”category_id”:”beauty”,”seq”:4,”pin_count”:41,”follow_count”:19,”like_count”:0,”created_at”:”1348629162″,”updated_at”:1361176634,”is_private”:0}}]
,”promotions”:null}

  html = page.read()

per = 100.0*blocknum*blocksize/totalsize

 错误的请求会返回空json
{“filter”:”pin:category:beauty”,”pins”:[],”promotions”:null}

  return html

if per > 100:

分析与实践中发现
对于这个一样的链接:
可以简化为
第一个参数是没用的变换值,从0-9,a-b变换。max是pin图片的id,你可以火狐查看,limit是请求max后面的多少张,目前限制最多100张。这样我们就清楚了。

def getImg(html):

per = 100

根据上面的链接读出json串,提取里面的key值(注意有2个key,后面的那个key是头像,我们不需要的),那就正则一下处理OK。
然后加上
设置id起点和终点    beauty的抓取模块是这样的:

  reg = r’src=”([^ >]+\.(?:jpeg|jpg))”‘

print ‘当前下载进度:%d’ %per

后来发现图片的类型可能有:jpeg,pjpeg,gif,bmp 做了下修改
保存成他原本的类型。

  html=html.decode(‘utf-8’)

r =
requests.get(‘)

#!/usr/bin/env python
# -*- encoding:utf-8 -*-
# author :insun
#

  imgre = re.compile(reg)

soup = BeautifulSoup(r.text, ‘html.parser’, from_encoding=’utf-8′)

import urllib,urllib2,re,sys,os
reload(sys)
sys.setdefaultencoding(‘utf-8’)

  imglist = re.findall(imgre, html)

i=0

#直接访问

  return imglist

for img in soup.find_all(‘img’):

#url = ”
if(os.path.exists(‘beauty’)
== False):
os.mkdir(‘beauty’)

ssl._create_default_https_context =
ssl._create_unverified_context

if img.has_attr(‘src’)!=None:

def
get_huaban_beauty():
start = 46284804
stop = 46285004
limit = 100
#他默认允许的limit为100
for i in range(start,stop):
url = ”
try:
i_headers = {“User-Agent”: “Mozilla/5.0(Windows; U; Windows
NT 5.1; zh-CN; rv:1.9.1)\
Gecko/20090624 Firefox/3.5″,\
“Referer”: ‘
req = urllib2.Request(url,
headers=i_headers)
html = urllib2.urlopen(req).read()

try:

url = img.get(‘src’)

reg = re.compile(‘”file”:{“farm”:”farm1″, \

  i=2178200;

urllib.urlretrieve(url,’.img’+str(i)+’.jpg’,Schedule)
i+=1

“bucket”:”hbimg”,.+?”key”:”(.*?)”,.+?”type”:”image/(.*?)”‘,re.S)
groups = re.findall(reg,html)

  while i<2178303:

urlretrieve(url,filename=None,reporthook=None,data=None)

for att in groups:
att_url = att[0]
img_type = att[1]
img_url = ”

    URL = “”

参数filename
指定了存储的本地路径(如果参数未指定,urllib会生成一个临时文件保存数据)

  • att_url
    urllib.urlretrieve(img_url,’beauty/’+att_url+’.’+img_type)
    print img_url +’.’+img_type + ‘
    download success!’
    except:
    print ‘error occurs’
    sys.exit(-1)

    print(URL)

参数reporthook是一个回调函数。当连接上服务器以及相应的数据块传输完毕时会触发该回调函数,我们可以利用这个回调函数来显示当前的下载速度

get_huaban_beauty()

    html = getHtml(URL)

 

图片 8

    print(getImg(html))

 

  x = 0

===========================================================================================

  for imgurl in getImg(html):

update 2013/03/01  鉴于最近抓取测试了一下和抱着科学研究的严谨态度
 发现上面的代码有一定问题。

   
urllib.request.urlretrieve(imgurl,’/Users/yangjunchao/imgdownload/%(id)d_%(number)s.jpg’
% {‘id’:i,’number’:x})

===========================================================================================

    x+=1

譬如说 
这样一个链接

    i=i+1

48112650  f3a6080bb44eccf688b767d6f7a2d16d793ffd7f15161-CbrPwz

except Exception as e:

48112395

  print(e)

48112339

48112322

48112178

48112140

48112129

此分类下得到的pin_id很难可能是连续的
因为同时有用户把图片pin到另一个分类  占据id

那么现在的思路就是根据max=48112770获取的json串里面最后一个id作为下个循环的max参数

 

另外发现

后缀都是固定的

列表小图

详情大图

我们要抓取清晰的大图  肯定要在后面加入_fw554后缀。

 

那么现在的代码是(可能还有其他bug):

 

#!/usr/bin/env python
# -*- encoding:utf-8 -*-
# author :insun
#
import urllib,urllib2,re,sys,os,time
reload(sys)
sys.setdefaultencoding(‘utf-8’)

#url = ”
if(os.path.exists(‘beauty’)
== False):
os.mkdir(‘beauty’)

def
get_huaban_beauty():
pin_id = 48145457
limit = 20
#他默认允许的limit为100
while pin_id != None:
url = ”
try:
i_headers = {“User-Agent”: “Mozilla/5.0(Windows; U; Windows
NT 5.1; zh-CN; rv:1.9.1)\
Gecko/20090624 Firefox/3.5″,\
“Referer”: ‘
req = urllib2.Request(url,
headers=i_headers)
html = urllib2.urlopen(req).read()

reg = re.compile(‘”pin_id”:(.*?),.+?”file”:{“farm”:”farm1″,
“bucket”:”hbimg”,.+?”key”:”(.*?)”,.+?”type”:”image/(.*?)”‘,re.S)
groups = re.findall(reg,html)
print str(pin_id)+ “Start
to catch “+str(len(groups))+”
photos”
for att in groups:
pin_id = att[0]
att_url = att[1]+’_fw554′
img_type = att[2]
img_url = ”

  • att_url
    if(urllib.urlretrieve(img_url,’beauty/’+att_url+’.’+img_type)):
    print img_url +’.’+img_type + ‘
    download success!’
    else:
    print img_url +’.’+img_type + ‘ save
    failed’
    #print pin_id
    except:
    print ‘error occurs’

 

get_huaban_beauty()

author: insun
title:Python抓取花瓣网图片脚本
blog:
**/ 花瓣网的架构: LVS + nginx reverse…

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图