Python 百度图片爬虫:高效下载图片,并提供详细解析
import argparse
import os
import re
import sys
import urllib
import json
import socket
import urllib.request
import urllib.parse
import urllib.error
# 设置超时
import time
timeout = 5
socket.setdefaulttimeout(timeout)
class Crawler:
# 睡眠时长
__time_sleep = 0.1
__amount = 0
__start_amount = 0
__counter = 0
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0', 'Cookie': ''}
__per_page = 30
# 获取图片url内容等
# t 下载图片时间间隔
def __init__(self, t=0.1):
self.time_sleep = t
# 获取后缀名
@staticmethod
def get_suffix(name):
m = re.search(r'\.[^\.]*$', name)
if m.group(0) and len(m.group(0)) <= 5:
return m.group(0)
else:
return '.jpeg'
@staticmethod
def handle_baidu_cookie(original_cookie, cookies):
'''
:param string original_cookie:
:param list cookies:
:return string:
'''
if not cookies:
return original_cookie
result = original_cookie
for cookie in cookies:
result += cookie.split(';')[0] + ';'
result.rstrip(';')
return result
# 保存图片
def save_image(self, rsp_data, word):
if not os.path.exists('./' + word):
os.mkdir('./' + word)
# 判断名字是否重复,获取图片长度
self.__counter = len(os.listdir('./' + word)) + 1
for image_info in rsp_data['data']:
try:
if 'replaceUrl' not in image_info or len(image_info['replaceUrl']) < 1:
continue
obj_url = image_info['replaceUrl'][0]['ObjUrl']
thumb_url = image_info['thumbURL']
url = 'https://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download&ie=utf8&fr=result&url=%s&thumburl=%s' % (urllib.parse.quote(obj_url), urllib.parse.quote(thumb_url))
time.sleep(self.time_sleep)
suffix = self.get_suffix(obj_url)
# 指定UA和referrer,减少403
opener = urllib.request.build_opener()
opener.addheaders = [
('User-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'),
]
urllib.request.install_opener(opener)
# 保存图片
filepath = './%s/%s' % (word, str(self.__counter) + str(suffix))
urllib.request.urlretrieve(url, filepath)
if os.path.getsize(filepath) < 5:
print('下载到了空文件,跳过!')
os.unlink(filepath)
continue
except urllib.error.HTTPError as urllib_err:
print(urllib_err)
continue
except Exception as err:
time.sleep(1)
print(err)
print('产生未知错误,放弃保存')
continue
else:
print('图+1,已有' + str(self.__counter) + '张图')
self.__counter += 1
return
# 开始获取
def get_images(self, word):
search = urllib.parse.quote(word)
# pn int 图片数
pn = self.__start_amount
while pn < self.__amount:
url = 'https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%s&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=&latest=©right=&word=%s&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=%s&rn=%d&gsm=1e&1594447993172=' % (search, search, str(pn), self.__per_page)
# 设置header防403
try:
time.sleep(self.time_sleep)
req = urllib.request.Request(url=url, headers=self.headers)
page = urllib.request.urlopen(req)
self.headers['Cookie'] = self.handle_baidu_cookie(self.headers['Cookie'], page.info().get_all('Set-Cookie'))
rsp = page.read()
page.close()
except UnicodeDecodeError as e:
print(e)
print('-----UnicodeDecodeErrorurl:', url)
except urllib.error.URLError as e:
print(e)
print('-----urlErrorurl:', url)
except socket.timeout as e:
print(e)
print('-----socket timout:', url)
else:
# 解析json
rsp_data = json.loads(rsp.replace(b'\\',b''))
if 'data' not in rsp_data:
print('触发了反爬机制,自动重试!')
else:
self.save_image(rsp_data, word)
# 读取下一页
print('下载下一页')
pn += self.__per_page
print('下载任务结束')
return
def start(self, word, total_page=1, start_page=1, per_page=30):
'''
爬虫入口
:param word: 抓取的关键词
:param total_page: 需要抓取数据页数 总抓取图片数量为 页数 x per_page
:param start_page:起始页码
:param per_page: 每页数量
:return:
'''
self.__per_page = per_page
self.__start_amount = (start_page - 1) * self.__per_page
self.__amount = total_page * self.__per_page + self.__start_amount
self.get_images(word)
if __name__ == '__main__':
if len(sys.argv) > 1:
parser = argparse.ArgumentParser()
parser.add_argument('-w', '--word', type=str, help='抓取关键词', required=True)
parser.add_argument('-tp', '--total_page', type=int, help='需要抓取的总页数', required=True)
parser.add_argument('-sp', '--start_page', type=int, help='起始页数', required=True)
parser.add_argument('-pp', '--per_page', type=int, help='每页大小', choices=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100], default=30, nargs='?')
parser.add_argument('-d', '--delay', type=float, help='抓取延时(间隔)', default=0.05)
args = parser.parse_args()
crawler = Crawler(args.delay)
crawler.start(args.word, args.total_page, args.start_page, args.per_page) # 抓取关键词为 “美女”,总数为 1 页(即总共 1*60=60 张),开始页码为 2
else:
# 如果不指定参数,那么程序会按照下面进行执行
crawler = Crawler(0.05) # 抓取延迟为 0.05
crawler.start('外滩', 60, 0, 50)
错误分析:
根据报错信息,发现是在解析json的时候出现了错误,具体是在第12行的第140个字符处缺少了一个逗号。可能是爬虫获取到的json数据格式不正确,需要检查一下爬虫代码中的json解析部分是否正确。
解决方法:
- 检查json数据格式: 打印出
rsp的内容,检查其是否为有效的json格式。可以使用json.dumps(rsp)将其格式化输出,方便查看。 - 处理异常数据: 在解析json之前,可以尝试对数据进行预处理,比如使用正则表达式去除多余的字符或空格,确保其符合json格式。
- 添加错误处理: 在
json.loads之后,可以添加错误处理,例如捕获json.decoder.JSONDecodeError异常,并在异常发生时进行相应的处理,例如打印错误信息、跳过该页或尝试重新获取数据。
代码优化建议:
- 使用 requests 库: requests 库比 urllib 库更加方便易用,可以简化代码,提高效率。
- 使用 try-except 块: 在网络请求和数据解析等易出错的地方添加 try-except 块,捕获异常并进行处理,提高程序的健壮性。
- 添加注释: 代码中添加注释,解释代码的功能和作用,方便理解和维护。
- 使用 lxml 库: 使用 lxml 库解析网页结构,可以提高解析效率和准确性。
- 使用代理 IP: 使用代理 IP 可以防止被网站识别为爬虫,提高抓取效率和稳定性。
注意:
爬取网站数据需要遵守网站的 robots.txt 协议,避免对网站造成负荷,同时也要注意版权问题。
原文地址: https://www.cveoy.top/t/topic/nMPw 著作权归作者所有。请勿转载和采集!