欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-07 19:52:42
...

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

scrapy startproject poco

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

scrapy genspider pocoSpider poco.com

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

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

python爬虫之动态网页爬取——poco爬虫
分析请求参数,猜想是否能够通过,修改参数来获得响应结果,但是修改请求参数后,请求失败,看来直接修改参数的方式不能拿到我们需要的url地址。

我们随意点进去个查看网页源代码,看看详情也是否也是js动态加载的页面。
python爬虫之动态网页爬取——poco爬虫
很幸运,可以在源代码里面直接查看到我们需要的数据,每张图片的地址就在 python爬虫之动态网页爬取——poco爬虫标签里面,那就简单了。我们可以先爬取这个页面的图片

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

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

写好selenium模拟操作的代码

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多条数据,我们现在重新来编写爬虫代码。

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方法来修改图片的存储路径

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

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来分类存储
python爬虫之动态网页爬取——poco爬虫
后记:其实爬虫代码中还存在一个bug,当爬虫运行10分钟并且id还未爬取完毕的情况下会自动停止,仔细检查代码,发现了一个mongodb的坑。那就是

 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中的数据存到列表中。

# -*- 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()
相关标签: 爬虫