zoukankan      html  css  js  c++  java
  • 【Python网络爬虫四】通过关键字爬取多张百度图片的图片

    最近看了女神的新剧《逃避虽然可耻但有用》,同样男主也是一名程序员,所以很有共鸣

    被大只萝莉萌的一脸一脸的,我们来爬一爬女神的皂片。

    百度搜索结果:新恒结衣

    本文主要分为4个部分:

      1.下载简单页面

      2.爬取多张图片

      3.页面解码

      4.爬取过程排错处理

    1.下载简单页面

    通过查看网页的html源码,分析得出,同一张图片共有4种链接:

    {"thumbURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=23&gp=0.jpg",
    "middleURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=21&gp=0.jpg",
    "hoverURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=23&gp=0.jpg",
    "objURL":"http://attachments.gfan.com/attachments2/day_110111/1101112033d77a4a8eb2b00eb1.jpg"}

    主要区别是分辨率不同,objURL是图片的源也是最清楚的一张。经测试,前三种都有反爬虫措施,用浏览器可以打开,但是刷新一次就403 Forbidden。用爬虫获取不到图片

    第四种objURL是指图片的源网址,获取该网址会出现三种情况:

    1. 正常。继续下载
    2. 403 Forbidden。用continue跳过。
    3. 出现异常。用try except处理。
     1 #coding: utf-8
     2 import os
     3 import re
     4 import urllib
     5 import urllib2
     6 
     7 def getHtml(url):
     8     page=urllib.urlopen(url)
     9     html=page.read()
    10     return html
    11 
    12 def getImg(html):
    13     reg=r'"objURL":"(.*?)"'   #正则
    14     # 括号表示分组,将括号的内容捕获到分组当中
    15     #  这个括号也就可以匹配网页中图片的url了
    16     imgre=re.compile(reg)
    17     print imgre
    18     imglist=re.findall(imgre,html)
    19     l=len(imglist)
    20     print l
    21     return imglist
    22 
    23 def downLoad(urls,path):
    24     index = 1
    25     for url in urls:
    26         print("Downloading:", url)
    27         try:
    28             res = urllib2.Request(url)
    29             if str(res.status_code)[0] == "4":
    30                 print("未下载成功:", url)
    31                 continue
    32         except Exception as e:
    33             print("未下载成功:", url)
    34         filename = os.path.join(path, str(index) + ".jpg")
    35         urllib.urlretrieve(url,filename)        #直接将远程数据下载到本地。
    36         index += 1
    37 html =getHtml("http://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&s"
    38               "f=1&fmq=1484296421424_R&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&"
    39               "word=新垣结衣&f=3&oq=xinyuanj&rsp=0")
    40 Savepath="D:TestDownload"
    41 downLoad(getImg(html),Savepath)

    其中urlretrieve方法
    直接将远程数据下载到本地。

            urllib.urlretrieve(url[, filename[, reporthook[, data]]])
            参数说明:
            url:外部或者本地url
            filename:指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据);
            reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。我们可以利用这个回调函数来显示当前的下载进度。
            data:指post到服务器的数据。该方法返回一个包含两个元素的元组(filename, headers),filename表示保存到本地的路径,header表示服务器的响应头。

    女神的照片就这么被爬下来了

    2.爬取更多图片

    等等,这个方法不过瘾呀,百度图片的页面是动态加载的,需要你往下滑才能继续加载后面的照片,我们这才完成了第一步,只爬到了30张图。

    打开浏览器,按F12,切换到Network标签,然后将网页向下拉。这时浏览器地址栏的网址并没有改变,而网页中的图片却一张张增加,说明网页在后台与服务器交互数据。好的发现了就是这个家伙

    XHR英文全名XmlHttpRequest,中文可以解释为可扩展超文本传输请求。Xml可扩展标记语言,Http超文本传输协议,Request请求。XMLHttpRequest对象可以在不向服务器提交整个页面的情况下,实现局部更新网页。当页面全部加载完毕后,客户端通过该对象向服务器请求数据,服务器端接受数据并处理后,向客户端反馈数据。

    点开对比Request的URL,可以发现,基本上是一样的,除了末尾一点点。

    只是pn的值不一样,测试发现,pn应该是表示当前请求的图片序号,rn表示更新显示图片的数量。

    pn=90&rn=30&gsm=5a&1484307466221=
    pn=120&rn=30&gsm=5a&1484307466223=
    pn=150&rn=30&gsm=78&1484307469213=
    pn=180&rn=30&gsm=78&1484307469214=
    pn=210&rn=30&gsm=b4&1484307553244=

    推荐一款Chrom的插件,Json handle。很方便。

    百度云下载地址。链接:http://pan.baidu.com/s/1qXZEjaW 密码:nxr5

     

    现在知道了,其实我们只要不断访问这个Request URL,改变他的pn值理论上就可以实现下载多张图片了。

    1 def getMoreURL(word):
    2     word = urllib.quote(word)
    3     url = r"http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&fp=result&queryWord={word}" 
    4           r"&cl=2&lm=-1&ie=utf-8&oe=utf-8&st=-1&ic=0&word={word}&face=0&istype=2nc=1&pn={pn}&rn=60"
    5     urls = (url.format(word=word, pn=x) for x in itertools.count(start=0, step=30))
    6     #itertools.count 0开始,步长30,迭代
    7     return urls

    其中,urllib.quote的作用是,对url就行处理。

    按照标准, URL 只允许一部分 ASCII 字符(数字字母和部分符号),其他的字符(如汉字)是不符合 URL 标准的。
    所以 URL 中使用其他字符就需要进行 URL 编码。

    URL 中传参数的部分(query String),格式是:
    name1=value1&name2=value2&name3=value3 
    
    假如你的 name 或者 value 值中有『&』或者『=』等符号,就当然会有问题。所以URL中的参数字符串也需要把『&=』等符号进行编码。

    URL编码的方式是把需要编码的字符转化为 %xx 的形式。通常 URL 编码是基于 UTF-8 的(当然这和浏览器平台有关)。
    例子:
    比如『我』,unicode 为 0x6211, UTF-8 编码为 0xE6 0x88 0x91,URL 编码就是 
    %E6%88%91

    itertools.count(start,step)的作用是一个迭代器,2个参数是开始和步长,这样就得到一个等差数列,这个数列填入url中,就得到了我们的urls。

    试了下,转到解析得到的数据,objURL居然不是一个http的图片,查资料,显示下一步要做的就是解码

    "objURL":"ippr_z2C$qAzdH3FAzdH3Fta_z&e3Bi1fsk_z&e3Bv54AzdH3FkufAzdH3Fw6vitejAzdH3F99lwbdublldlnvjaavmc8c01ba8a8m1mu0vjwama_z&e3B3r2"

    3.页面解码

    参考百度图片页面解码,发现其实就是一个table,key和value对应就可以解码了,简直就是明文密码0.0.。。。

    所以在我们的程序里加个字典,多个解码的过程。

    期间解码过程一度出错,经过仔细排查,是url.translate(char_table)出了问题,

    str 的translate方法需要用单个字符的十进制unicode编码作为key 
    value 中的数字会被当成十进制unicode编码转换成字符 
    也可以直接用字符串作为value

    所以需要加上一行

    char_table = {ord(key): ord(value) for key, value in char_table.items()}

     然而试了还是不行,提示translate方法有问题,去查了一下官方的文档

    里面的例子是maketrans()方法一起用的,例子是替换元音字母

    #!/usr/bin/python
    
    from string import maketrans   # 引用 maketrans 函数。
    
    intab = "aeiou"
    outtab = "12345"
    trantab = maketrans(intab, outtab)
    
    str = "this is string example....wow!!!";
    print str.translate(trantab);

    输出结果:

    1 th3s 3s str3ng 2x1mpl2....w4w!!!

    所以对我们的chartable做处理,将key和value 通过maketrans关联:

        intab="wkv1ju2it3hs4g5rq6fp7eo8dn9cm0bla"
        outtab="abcdefghijklmnopqrstuvw1234567890"
        trantab = maketrans(intab, outtab)

    到此为止,成功解析图片url。

     4.爬取过程排错处理

    真正在爬取的时候,会有一些错误,什么404,403啊,这样的话,程序就爬不到图片,这就比较尴尬了。

    所以在下载的时候,需要做处理。

    开始的时候,我用的是这种:

    1 try:  
    2         res = requests.get(imgUrl, timeout=10)  #超时
    3         if str(res.status_code)[0] == "4":  
    4             print(str(res.status_code), ":" , imgUrl)  
    5             return False  
    6     except Exception as e:  
    7         print("抛出异常:", imgUrl)  
    8         print(e)

    但是爬着爬着发现,有的时候status_code不存在,又调试了一番,通过对比,发现404,403这种属于HTTPErro,但是request的时候,还可能产生URLError,如下图。

    所以抛出异常的过程就需要进行分开处理了。

    10060 因为目标主机主动拒绝,连接不能建立。这通常是因为试图连接到一个远程主机上不活动的服务,如没有服务器应用程序处于执行状态

     1 res = urllib2.Request(url)
     2         try:
     3             response = urllib2.urlopen(res ,data=None, timeout=5)  #超时处理
     4         except urllib2.URLError, e:
     5             if hasattr(e,'code'):
     6                 error_status = e.code
     7                 print(error_status, "未下载成功:", url)
     8                 continue
     9             elif hasattr(e,'reason'):
    10                 print( "time out", url)
    11                 continue

    测试成功,可以通过自己设置下载图片数量,然后无线爬了,爬的速度差不多2分钟爬了300多张,这和图片的源和大小都有一定关系。

    All Code:

      1 #coding: utf-8
      2 import os
      3 import re
      4 import urllib
      5 import urllib2
      6 import itertools  #迭代器
      7 from string import maketrans
      8 
      9 str_table = {
     10     '_z2C$q': ':',
     11     '_z&e3B': '.',
     12     'AzdH3F': '/'
     13 }
     14 
     15 char_table = {
     16     'w': 'a',
     17     'k': 'b',
     18     'v': 'c',
     19     '1': 'd',
     20     'j': 'e',
     21     'u': 'f',
     22     '2': 'g',
     23     'i': 'h',
     24     't': 'i',
     25     '3': 'j',
     26     'h': 'k',
     27     's': 'l',
     28     '4': 'm',
     29     'g': 'n',
     30     '5': 'o',
     31     'r': 'p',
     32     'q': 'q',
     33     '6': 'r',
     34     'f': 's',
     35     'p': 't',
     36     '7': 'u',
     37     'e': 'v',
     38     'o': 'w',
     39     '8': '1',
     40     'd': '2',
     41     'n': '3',
     42     '9': '4',
     43     'c': '5',
     44     'm': '6',
     45     '0': '7',
     46     'b': '8',
     47     'l': '9',
     48     'a': '0'
     49 }
     50 
     51 intab="wkv1ju2it3hs4g5rq6fp7eo8dn9cm0bla"
     52 outtab="abcdefghijklmnopqrstuvw1234567890"
     53 trantab = maketrans(intab, outtab)
     54 
     55 char_table = {ord(key): ord(value) for key, value in char_table.items()}
     56 def deCode(url):
     57     # 先替换字符串
     58     for key, value in str_table.items():
     59         url = url.replace(key, value)
     60     # 再替换剩下的字符
     61     d=url.translate(trantab)
     62     return d
     63 
     64 def getMoreURL(word):
     65     word = urllib.quote(word)
     66     url = r"http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&fp=result&queryWord={word}" 
     67           r"&cl=2&lm=-1&ie=utf-8&oe=utf-8&st=-1&ic=0&word={word}&face=0&istype=2nc=1&pn={pn}&rn=30"
     68     urls = (url.format(word=word, pn=x) for x in itertools.count(start=0, step=30))
     69     #itertools.count 0开始,步长30,迭代
     70     return urls
     71 
     72 def getHtml(url):
     73     page=urllib.urlopen(url)
     74     html=page.read()
     75     return html
     76 
     77 #解析图片url解码
     78 def getImg(html):
     79     reg=r'"objURL":"(.*?)"'   #正则
     80     # 括号表示分组,将括号的内容捕获到分组当中
     81     #  这个括号也就可以匹配网页中图片的url了
     82     imgre=re.compile(reg)
     83     imageList=re.findall(imgre, html)
     84     imgUrls=[]
     85 
     86     for image in imageList:
     87         imgUrls.append(deCode(image))
     88 
     89     l=len(imgUrls)
     90     print l
     91     return imgUrls
     92 
     93 def downLoad(urls,path):
     94     global index
     95     for url in urls:
     96         print("Downloading:", url)
     97         res = urllib2.Request(url)
     98         try:
     99             response = urllib2.urlopen(res ,data=None, timeout=5)  #超时处理
    100         except urllib2.URLError, e:
    101             if hasattr(e,'code'):
    102                 error_status = e.code
    103                 print(error_status, "未下载成功:", url)
    104                 continue
    105             elif hasattr(e,'reason'):
    106                 print( "time out", url)
    107                 continue
    108 
    109             continue
    110 
    111         filename = os.path.join(path, str(index) + ".jpg")
    112         urllib.urlretrieve(url,filename)        #直接将远程数据下载到本地。
    113         index += 1
    114         # urllib.urlretrieve(url[, filename[, reporthook[, data]]])
    115         # 参数说明:
    116         # url:外部或者本地url
    117         # filename:指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据);
    118         # reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。我们可以利用这个回调函数来显示当前的下载进度。
    119         # data:指post到服务器的数据。该方法返回一个包含两个元素的元组(filename, headers),filename表示保存到本地的路径,header表示服务器的响应头。
    120         if index-1==1000:
    121             break
    122 
    123 if __name__ == '__main__':
    124     keyWord="新垣结衣"
    125     index = 1
    126     Savepath = "D:TestDownload"
    127     urls=getMoreURL(keyWord)
    128 
    129     for url in urls:
    130         downLoad(getImg(getHtml(url)),Savepath)
    131         if index-1==1000:
    132             break
    View Code
  • 相关阅读:
    arcgis api 3.x for js 入门开发系列八聚合效果(附源码下载)
    arcgis api 3.x for js 入门开发系列七图层控制(附源码下载)
    arcgis api 3.x for js 入门开发系列六地图分屏对比(附源码下载)
    arcgis api 3.x for js 入门开发系列五地图态势标绘(附源码下载)
    arcgis api 3.x for js 入门开发系列四地图查询(附源码下载)
    Java里面获取当前服务器的IP地址
    Flutter at Google I/O 2018
    Modbus RTU 协议使用汇总
    plsql 创建表空间、用户、赋予权限
    Oracle:ODP.NET Managed 小试牛刀
  • 原文地址:https://www.cnblogs.com/SeekHit/p/6284974.html
Copyright © 2011-2022 走看看