今天记录海外图片素材网站Pinterest的图片爬取
初步分析:网站是动态渲染网站,每往下滑动,刷新出来新的页面;通过观察刷新后的网页,初步定位每页的请求url是https://www.pinterest.com/resource/BaseSearchResource/get/,可以看到这是一个POST请求,
之后分析Preview和Response可以确定该url里面的确有我们要的数据:
但是该POST请求有两个加密参数:source_url和data;经对比后,发现每页数据的source_url是相同的,data的属性里面,除了bookmarks不同,其他属性也全部相同。
初步思路是通过js逆向解析出bookmarks,然后得到每一页直接的规律;但是!!!该属性逆向分析起来实在很难,对于不会JS的人,断点打着打着就不知道自己的目标变量跑哪里去了。最后放弃逆向解析的方法,改用selenium实现。
确定思路:决定用selenium之后,思路如下:先通过selenium打开网页,网页链接为:‘https://www.pinterest.com/search/pins/?q=关键词’,然后通过元素定位到图片链接;最重要的,调用driver.execute_script()方法来实现页面滑动(这里需要提前调试获取每次滑动高度的最佳值),然后再次定位新页面的元素,找到图片链接;对于重复的进行去重;直到滑到底部之后停止滑动,将定位的所有图片链接保存至.txt文档,以便后续下载图片。
完整代码和注释如下:
以下是获取图片链接并保存到本地:
from selenium import webdriver
from selenium.webdriver import Chrome
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
import json
import time
cr_options = ChromeOptions()
cr_options.add_argument("--start-maximized") #将浏览器最大化
# Input keywords
queries = [ # 想要搜索分类的关键词
# '頭貼 卡通',
'动物 头像',
# '可愛頭貼',
# '文字 头像',
# '人物 头像',
# 'ins风 头像',
# '油画 头像',
# '摄影 风景',
]
driver = Chrome('chromedriver.exe', options=cr_options)
print('Size: ' + str(driver.get_window_size()))
# 这里输出浏览器尺寸是为了后面调试每次滑动的最佳高度
original_urls = set() # 将图片的原始链接存储在集合内,自动去重
lasts = []
for query in queries:
base_url = f'https://www.pinterest.com/search/pins/?q={query}'
driver.get(base_url)
time.sleep(2)
for i in range(30): # 这里为了保证滑到底部,所以循环次数写到了30,但其实可能不需要30次
print('i----------',i) # 输出i来记录滑动了几次(翻了几页)
if i < 4:
all_elements = driver.find_elements(By.XPATH, '//div[@data-test-id="pin-visual-wrapper"]/div[1]/div[1]/img') # 查找全部的图片元素
print(len(all_elements))
print('-------------------------------------------------------------------')
# print(all_elements)
for element in all_elements:
img_url = element.get_attribute('src') # 获取图片链接
original_url = img_url.replace('236x', 'originals') #缩略图的尺寸只有236x,替换为originals才是原图尺寸高清
original_urls.add(original_url)
last = img_url
# print(img_url)
# print(original_url)
# break
# print('*****************************************')
print(last)
lasts.append(last)
driver.execute_script(f'window.scrollTo({i * 2000},{(i + 1) * 2000})')
# 设置每次滑动的高度,我这里经调试后即使每次滑动2000,每页数据也还是会有重复,所以必须去重
time.sleep(3)
else:
all_elements = driver.find_elements(By.XPATH, '//div[@data-test-id="pin-visual-wrapper"]/div[1]/div[1]/img')
print(len(all_elements))
print('------------------------------------------------------------')
for element in all_elements:
img_url = element.get_attribute('src')
original_url = img_url.replace('236x', 'originals')
original_urls.add(original_url)
last = img_url
print(last)
lasts.append(last)
if lasts[-1] == lasts[-2] and lasts[-1] == lasts[-3]:
# 这一步操作是为了判断滑到了底部,以last来记录每一页的最后一个数据,如果连续3页数据的最后一个数据都相同,则代表滑到了底部,则停止循环
break
else:
driver.execute_script(f'window.scrollTo({i * 2000},{(i + 1) * 2000})')
# 如果没有滑到底部则继续循环
time.sleep(3)
print('lasts: ',lasts)
print('*****************************************')
# print(original_urls)
print(len(original_urls))
for item in original_urls:
with open('ddd.txt','a+') as f: # 最后将爬取的链接写入ddd.txt,注意要是a+追加方式,因为该代码可能要跑几遍(毕竟科学上网不稳定,可能有图片第一遍没有获取成功)
f.writelines(item + "\n")
以下是读取保存的图片链接,然后将图片下载下来(该操作和上面操作分开来主要是本人有几个分类要爬取,不同分类图片的链接也放在不同文件下,所以分开进行;只需要爬取一个分类的同学可以一起进行):
import requests
import time
# '插画头像',
# '风景头像',
# '可爱头像',
# '文字头像',
# '人物头像',
# '油画头像',
# '设计头像',
# '动物头像',
def downfile(file, url): # 下载图片方法
print("开始下载:", url)
try:
r = requests.get(url, stream=True,timeout=30)
# 默认情况下是stream=False,会立即开始下载文件并存放到内存当中,倘若文件过大就会导致内存不足的情况.
# 当把stream参数设置成True时,不会立即开始下载,当使用iter_content或iter_lines遍历内容或访问内容属性时才开始下载。
with open(file, 'wb') as fd:
for chunk in r.iter_content(): # iter_content:一块一块的遍历要下载的内容
fd.write(chunk)
except Exception as e: #提前做好错误检查,出错时可以跳过该条继续后面的;然后多跑几次就好了
print("下载失败了", e)
urls = []
with open("D:/python/code/Test//bak0/油画头像.txt", "r+") as f:
for line in f.readlines():
urls.append(line.rstrip())
for url in urls:
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36'}
PWD = 'D:/python/code/TestScrapy/img/Pinterest/油画头像/'
name = url.split('/')[-1]
# 将图片以最后的数字+后缀命名,方便检查文件是否存在
filename = PWD + name
if os.path.isfile(filename): # 如果文件已爬取,则不再重复爬取
print("文件存在:", filename)
continue
try:
r = requests.get(url, headers=headers, stream=True,timeout=30)
except Exception as e: # 同样提前做好错误检查,出错时可以跳过该条继续后面的;然后多跑几次就好了
print("请求失败了", e)
continue
# url = url.replace('.jpg','.png')
#这里的坑在于,有一些图片缩略图是.jpg格式,原图又是.png格式;
#所以加这一行的作用是为了在爬完所有.jpg之后,如果发现有.jpg图片打不开,则是因为该图片格式错误;
#需要手动将文件夹内打不开的图片删除,然后将这行代码取消注释,再跑几遍,直到文件夹内所有图片都可以打开。
downfile(filename, url)
print('--------------------------------')
print('over!')