python爬虫爬取百度图片

我们平时经常会有一些搜集数据的需要,尤其是图片数据。如果一个一个从网上找再下载下来实在是太麻烦了,这么繁琐的工作不如交给脚本去做。于是我写了一个简单的PYTHON3爬取百度图片的爬虫,github: https://github.com/plutojia/crawler-for-baiduImage

我们先打开百度图片看看它到底是什么样子的:
在其中搜索“美女”:

我们在浏览这些图片时可以发现,当你下拉滚动条时,又会有新的图片出现,而这些新的图片并不是一开始就加载好的,而是随着你的向下浏览不断请求刷新的,这就说明百度图片这种瀑布流式加载使用了Ajax技术,那么我们看看它的Ajax请求到底是什么样的。
在搜索美女的页面下,打开chrome浏览器的开发者工具,选择Network选项卡,再选择Network选项卡里的XHR选项卡,然后把网页向下翻,多翻几页,应该会看到请求出现,如图所示:

双击点开其中的任意请求看看:

发现请求url是https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%BE%8E%E5%A5%B3&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&word=%E7%BE%8E%E5%A5%B3&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&cg=girl&pn=120&rn=30&gsm=78&1537365676312=

前面的https://image.baidu.com/search/acjson?还挺清楚,后面的一长串是什么玩意啊?别急,继续往下看。在开发者工具中继续往下查看,可以看到有这么一段:

这里的Query String Parameters其实就是我们的请求信息,其中好多都是固定的,需要注意的只有几个:queryWord和word是你的搜索关键字,rn代表一页有多少图,一般取30,pn代表已经显示了多少图,取30*n即可。这样我们对请求的分析就搞定啦,写个代码试一下

首先先import一些常用模块


  
  
  1. from urllib.parse import urlencode
  2. import requests
  3. import re
  4. import os
  5. def get_page(offset):
  6. params = {
  7. 'tn': 'resultjson_com',
  8. 'ipn': 'rj',
  9. 'ct': '201326592',
  10. 'is': '',
  11. 'fp': 'result',
  12. 'queryWord': '帅哥',
  13. 'cl': '2',
  14. 'lm': '-1',
  15. 'ie': 'utf-8',
  16. 'oe': 'utf-8',
  17. 'adpicid': '',
  18. 'st': '-1',
  19. 'z': '',
  20. 'ic': '0',
  21. 'word': '帅哥',
  22. 's': '',
  23. 'se': '',
  24. 'tab': '',
  25. 'width': '',
  26. 'height': '',
  27. 'face': '0',
  28. 'istype': '2',
  29. 'qc': '',
  30. 'nc': '1',
  31. 'fr': '',
  32. 'expermode': '',
  33. 'cg': 'girl',
  34. 'pn': offset* 30,
  35. 'rn': '30',
  36. 'gsm': '1e',
  37. '1537355234668': '',
  38. }
  39. url = 'https://image.baidu.com/search/acjson?' + urlencode(params)
  40. try:
  41. response = requests.get(url)
  42. if response.status_code == 200:
  43. return response.json()
  44. except requests.ConnectionError as e:
  45. print( 'Error', e.args)
  46. if __name__== '__main__':
  47. json = get_page( 1)
  48. print(json)

运行以上代码,输出即为响应的JSON形式,若能成功输出,则请求正确。我们还需要从其中获得图片的地址,过程如下:
在开发者工具中,选择Preview,点开其中的data,发现一堆0,1,2,3....这些就代表了各个图片及其信息,我们点开一个看看,比如点开0:

这里面的objURL就是图片的真实地址了,而且是未经压缩的大图哦!等等,这地址怎么看着这么奇怪,这是因为百度对其进行了加密,解密只需要一个函数:


  
  
  1. def baidtu_uncomplie(url):
  2. res = ''
  3. c = [ '_z2C$q', '_z&e3B', 'AzdH3F']
  4. d= { 'w': 'a', 'k': 'b', 'v': 'c', '1': 'd', 'j': 'e', 'u': 'f', '2': 'g', 'i': 'h', 't': 'i', '3': 'j', 'h': 'k', 's': 'l', '4': 'm', 'g': 'n', '5': 'o', 'r': 'p', 'q': 'q', '6': 'r', 'f': 's', 'p': 't', '7': 'u', 'e': 'v', 'o': 'w', '8': '1', 'd': '2', 'n': '3', '9': '4', 'c': '5', 'm': '6', '0': '7', 'b': '8', 'l': '9', 'a': '0', '_z2C$q': ':', '_z&e3B': '.', 'AzdH3F': '/'}
  5. if(url== None or 'http' in url):
  6. return url
  7. else:
  8. j= url
  9. for m in c:
  10. j=j.replace(m,d[m])
  11. for char in j:
  12. if re.match( '^[a-w\d]+$',char):
  13. char = d[char]
  14. res= res+char
  15. return res

只需将objURL作为参数传进去就能返回图片真实地址。那么现在我们只需要写一个能分析响应从而得到objURL的函数,再将objURL变成真实地址,最后将图片下载下来就好了。已经没什么难的了,直接上完整代码:


  
  
  1. from urllib.parse import urlencode
  2. import requests
  3. import re
  4. import os
  5. save_dir= 'baidutu/'
  6. def baidtu_uncomplie(url):
  7. res = ''
  8. c = [ '_z2C$q', '_z&e3B', 'AzdH3F']
  9. d= { 'w': 'a', 'k': 'b', 'v': 'c', '1': 'd', 'j': 'e', 'u': 'f', '2': 'g', 'i': 'h', 't': 'i', '3': 'j', 'h': 'k', 's': 'l', '4': 'm', 'g': 'n', '5': 'o', 'r': 'p', 'q': 'q', '6': 'r', 'f': 's', 'p': 't', '7': 'u', 'e': 'v', 'o': 'w', '8': '1', 'd': '2', 'n': '3', '9': '4', 'c': '5', 'm': '6', '0': '7', 'b': '8', 'l': '9', 'a': '0', '_z2C$q': ':', '_z&e3B': '.', 'AzdH3F': '/'}
  10. if(url== None or 'http' in url):
  11. return url
  12. else:
  13. j= url
  14. for m in c:
  15. j=j.replace(m,d[m])
  16. for char in j:
  17. if re.match( '^[a-w\d]+$',char):
  18. char = d[char]
  19. res= res+char
  20. return res
  21. def get_page(offset):
  22. params = {
  23. 'tn': 'resultjson_com',
  24. 'ipn': 'rj',
  25. 'ct': '201326592',
  26. 'is': '',
  27. 'fp': 'result',
  28. 'queryWord': '帅哥',
  29. 'cl': '2',
  30. 'lm': '-1',
  31. 'ie': 'utf-8',
  32. 'oe': 'utf-8',
  33. 'adpicid': '',
  34. 'st': '-1',
  35. 'z': '',
  36. 'ic': '0',
  37. 'word': '帅哥',
  38. 's': '',
  39. 'se': '',
  40. 'tab': '',
  41. 'width': '',
  42. 'height': '',
  43. 'face': '0',
  44. 'istype': '2',
  45. 'qc': '',
  46. 'nc': '1',
  47. 'fr': '',
  48. 'expermode': '',
  49. 'pn': offset* 30,
  50. 'rn': '30',
  51. 'gsm': '1e',
  52. '1537355234668': '',
  53. }
  54. url = 'https://image.baidu.com/search/acjson?' + urlencode(params)
  55. try:
  56. response = requests.get(url)
  57. if response.status_code == 200:
  58. return response.json()
  59. except requests.ConnectionError as e:
  60. print( 'Error', e.args)
  61. def get_images(json):
  62. if json.get( 'data'):
  63. for item in json.get( 'data'):
  64. if item.get( 'fromPageTitle'):
  65. title = item.get( 'fromPageTitle')
  66. else:
  67. title= 'noTitle'
  68. image = baidtu_uncomplie(item.get( 'objURL'))
  69. if(image):
  70. yield {
  71. 'image': image,
  72. 'title': title
  73. }
  74. def save_image(item,count):
  75. try:
  76. response = requests.get(item.get( 'image'))
  77. if response.status_code == 200:
  78. file_path = save_dir+ '{0}.{1}'.format(str(count), 'jpg')
  79. if not os.path.exists(file_path):
  80. with open(file_path, 'wb') as f:
  81. f.write(response.content)
  82. else:
  83. print( 'Already Downloaded', file_path)
  84. except requests.ConnectionError:
  85. print( 'Failed to Save Image')
  86. def main(pageIndex,count):
  87. json = get_page(pageIndex)
  88. for image in get_images(json):
  89. save_image(image, count)
  90. count += 1
  91. return count
  92. if __name__== '__main__':
  93. if not os.path.exists(save_dir):
  94. os.mkdir(save_dir)
  95. count= 1
  96. for i in range( 1, 20):
  97. count=main(i,count)
  98. print( 'total:',count)

最后爬下来的图片会以1,2,3,4~命名,最终输出图片总数。

猜你喜欢

转载自blog.csdn.net/qq_36827625/article/details/84989115