python爬虫之动态网页爬取——poco爬虫

LOVELETTERD·2023-04-03 22:49·203 次阅读

python爬虫之动态网页爬取——poco爬虫

背景#

做这个一个爬虫源于项目中需要做ai模型训练需要用到大量的同一人的不同场景下的照片,正好这种摄影网站里面可以找到。

项目初始化#

我们先创建一个爬虫项目,这里我们用scrapy框架来创建。

Copy
scrapy startproject poco

然后cd 到 poco文件夹中初始化一下项目

Copy
scrapy genspider pocoSpider poco.com

项目结构#

打开项目,项目目录结构如下

在这里插入图片描述

代码分析#

我们的爬虫代码就写在pocoSpider文件中,现在我们打开网站分析一下网页。
我们选择人像分类来爬取
https://www.poco.cn/works/works_list?classify_type=1&works_type=medal
可以看到页面是有很多用户id,我们要先拿到每个id的url再进去详情页抓取图片。

右键查看网页源代码,发现页面是有js动态生成的网页
在这里插入图片描述
而且页面是懒加载的,右键审查元素,查看网络
在这里插入图片描述
找到请求图片的请,拿到请求参数,url地址,复制到postman里面调用一下,成功拿到返回数据

在这里插入图片描述
分析请求参数,猜想是否能够通过,修改参数来获得响应结果,但是修改请求参数后,请求失败,看来直接修改参数的方式不能拿到我们需要的url地址。

我们随意点进去个查看网页源代码,看看详情也是否也是js动态加载的页面。

在这里插入图片描述

很幸运,可以在源代码里面直接查看到我们需要的数据,每张图片的地址就在 标签里面,那就简单了。我们可以先爬取这个页面的图片

Copy
import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings class PocospiderSpider(scrapy.Spider): name = 'pocoSpider' allowed_domains = ['poco.com'] start_urls = ['https://www.poco.cn/works/detail?works_id=20992476'] def parse(self, response): img_list = response.xpath("//img/@data-src").extract() for img in img_list: print(img) if __name__ == "__main__": process = CrawlerProcess(get_project_settings()) process.crawl('pocoSpider') process.start()

如设想的一样,我们成功拿到了当前id里面的所有图片:
在这里插入图片描述

我们现在只需要拿到所有id对应的url地址再循环请求就能拿到所有图片的地址了
既然通过修改参数的方式不能够拿到数据,那我们就用selenium框架来模拟浏览器操作。
分析页面,当每次下拉到最后发送请求。我们关闭页面的图片加载,以便加速访问.我们把爬取到的数据存储到数据库中,这里我们用mongod。定义下拉刷新的函数,execute_times 每次刷新后睡眠0.5秒以便浏览器渲染页面。我们这里刷新40次来获得数据。
我们在setting.py文件中设置好数据库的地址,端口,数据库名称

Copy
LOCAL_MONGO_HOST = '127.0.0.1' LOCAL_MONGO_PORT = 27017 DB_NAME = 'POCO'

写好selenium模拟操作的代码

Copy
from pymongo.errors import DuplicateKeyError from selenium import webdriver import time from lxml import etree import pymongo from poco.settings import LOCAL_MONGO_HOST,LOCAL_MONGO_PORT,DB_NAME chrome_opt = webdriver.ChromeOptions() prefs = {'profile.managed_default_content_settings.images': 2} chrome_opt.add_experimental_option('prefs',prefs) driver = webdriver.Chrome(chrome_options=chrome_opt) driver.get("https://www.poco.cn/works/works_list?classify_type=1&works_type=editor") time.sleep(0.5) def execute_times(times): for i in range(times + 1): driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(0.5) execute_times(40) html = driver.page_source xpath_html = etree.HTML(html) a_list = xpath_html.xpath("//div[@class='vw_works_list']/a/@href") print(len(a_list)) mongo_client = pymongo.MongoClient(LOCAL_MONGO_HOST, LOCAL_MONGO_PORT) collection = mongo_client[DB_NAME]["idlist"] for a in a_list: id = a.split("?")[-1].split("=")[-1] try: collection.insert_one({"_id":id,"url":a}) except DuplicateKeyError as e: collection.find_one_and_update({"_id":id},{"$set":{"url":a}}) driver.close()

执行完毕我们大概能拿到800多条数据,我们现在重新来编写爬虫代码。

Copy
import pymongo import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from poco.settings import LOCAL_MONGO_HOST,LOCAL_MONGO_PORT,DB_NAME class PocospiderSpider(scrapy.Spider): name = 'pocoSpider' mongo_client = pymongo.MongoClient(LOCAL_MONGO_HOST, LOCAL_MONGO_PORT) collection = mongo_client[DB_NAME]["idlist"] id_list = collection.find() def start_requests(self): for id in self.id_list: item = {} item["folder"]=id["_id"] item["img_urls"]=[] yield scrapy.Request(url=id["url"],callback=self.parse_detail,meta={"item":item}) def parse_detail(self, response): item = response.meta["item"] img_list = response.xpath("//img/@data-src").extract() for img in img_list: if img == "": continue img = "https:"+img item["img_urls"].append(img) yield item if __name__ == "__main__": process = CrawlerProcess(get_project_settings()) process.crawl('pocoSpider') process.start()

我们scrapy的pipelines来下载图片,我们先写一个pipeline方法,将图片分id来进行存储,我们重写file_path方法来修改图片的存储路径

Copy
from scrapy.exceptions import DropItem from scrapy.pipelines.images import ImagesPipeline from scrapy import Request class MyImagesPipeline(ImagesPipeline): def get_media_requests(self, item, info): for image_url in item['img_urls']: referer = image_url yield Request(image_url,meta={'item':item,'referer':referer}) def file_path(self, request, response=None, info=None): item = request.meta['item'] folder = item['folder'].strip() img_name = request.url.split("/")[-1] filename = u'img/{0}/{1}'.format(folder,img_name) return filename def item_completed(self, results, item, info): image_path = [x['path'] for ok,x in results if ok] if not image_path: raise DropItem('Item contains no images') # item['image_paths'] = image_path return item

最后我们在setting文件中修改图片的下载中间件,以及PipeLines

Copy
DOWNLOADER_MIDDLEWARES = { 'poco.middlewares.PocoDownloaderMiddleware': 543, } IMAGES_STORE=r'E:\\' IMAGES_EXPIRES = 30 # Enable or disable extensions # See https://doc.scrapy.org/en/latest/topics/extensions.html #EXTENSIONS = { # 'scrapy.extensions.telnet.TelnetConsole': None, #} ITEM_PIPELINES = { 'poco.pipelines.MyImagesPipeline': 300, } CONCURRENT_REQUESTS = 32 DOWNLOAD_DELAY = 0.1 # Configure item pipeli

至此poco网站的爬虫已经编写完毕,运行代码,已经成功爬取图片,并且按照id来分类存储

在这里插入图片描述

后记#

其实爬虫代码中还存在一个bug,当爬虫运行10分钟并且id还未爬取完毕的情况下会自动停止,仔细检查代码,发现了一个mongodb的坑。那就是

Copy
collection = mongo_client[DB_NAME]["idlist"] id_list = collection.find()

db.collection.find() 的时候,它返回的不是所有的数据,而实际上是一个“cursor”。它的默认行为是:第一次向数据库查询 101 个文档,或 1 MB 的文档,取决于哪个条件先满足;之后每次 cursor 中的文档用尽后,查询 4 MB 的文档。另外,find() 的默认行为是返回一个 10 分钟无操作后超时的 cursor。如果10分钟没有处理完请求,那我们也就拿不到剩余的url,所以爬虫也就停止了。
解决办法也很简单,那就是最开始的时候直接将id_list中的数据存到列表中。

Copy
# -*- coding: utf-8 -*- import pymongo import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from poco.settings import LOCAL_MONGO_HOST,LOCAL_MONGO_PORT,DB_NAME class PocospiderSpider(scrapy.Spider): name = 'pocoSpider' mongo_client = pymongo.MongoClient(LOCAL_MONGO_HOST, LOCAL_MONGO_PORT) collection = mongo_client[DB_NAME]["idlist"] id_list = collection.find() ids=[] for id in id_list: ids.append(id) def start_requests(self): for id in self.ids: item = {} item["folder"]=id["_id"] item["img_urls"]=[] yield scrapy.Request(url=id["url"],callback=self.parse_detail,meta={"item":item}) def parse_detail(self, response): item = response.meta["item"] img_list = response.xpath("//img/@data-src").extract() for img in img_list: if img == "": continue img = "https:"+img item["img_urls"].append(img) yield item if __name__ == "__main__": process = CrawlerProcess(get_project_settings()) process.crawl('pocoSpider') process.start()
posted @   loveletters  阅读(206)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· spring官宣接入deepseek,真的太香了~
点击右上角即可分享
微信分享提示
目录