动态网页抓取(一个爬虫项目目录结构(一)--一个 )
优采云 发布时间: 2022-04-04 20:08动态网页抓取(一个爬虫项目目录结构(一)--一个
)
我们首先创建一个爬虫项目,这里我们使用scrapy框架来创建它。
scrapy startproject poco
然后cd到poco文件夹初始化项目
scrapy genspider pocoSpider poco.com
打开项目,项目目录结构如下
我们的爬虫代码写在 pocoSpider 文件中,现在我们打开 网站 来分析网页。
我们选择人像分类爬行
可以看到页面上有很多用户id。我们需要先获取每个id的url,然后去详情页抓图。
右键查看网页源码,发现该页面是js动态生成的网页
并且页面是懒加载的,右键检查元素,查看网络
如果找到请求的图片,请获取请求参数,url地址,复制到postman中调用,成功获取返回数据
分析请求参数,猜测是否可以通过,修改参数得到响应结果,但是修改请求参数后,请求失败,看来直接修改参数的方法无法得到我们需要的url地址。
让我们随意点击查看网页源代码,看看细节是否也是js动态加载的。
好在我们需要的数据可以直接在源码中查看,每张图片的地址都在标签里面,很简单。我们可以先爬取这个页面的图片
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 sleep 0.5秒,以便浏览器渲染页面。这里我们刷新 40 次来获取数据。
我们在setting.py文件中设置数据库的地址、端口、数据库名
LOCAL_MONGO_HOST = '127.0.0.1'
LOCAL_MONGO_PORT = 27017
DB_NAME = 'POCO'
编写 selenium 模拟操作的代码
执行完之后大概能拿到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管道下载图片,我们先写一个管道方法通过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
最后我们修改设置文件中的图片下载中间件和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分类存储
后记:其实爬虫代码还是有bug的。当爬虫运行 10 分钟,没有爬取到 id 时,会自动停止。仔细查看代码,发现一个mongodb的坑。那是
collection = mongo_client[DB_NAME]["idlist"]
id_list = collection.find()
当调用 db.采集.find() 时,它返回的不是所有数据,而是一个“光标”。它的默认行为是:首先在数据库中查询 101 个文档,或者 1 MB 的文档,这取决于首先满足哪个条件;然后每次游标用完时,查询 4 MB 的文档。此外,find() 的默认行为是返回一个在 10 分钟不活动后超时的游标。如果 10 分钟内没有处理请求,那么我们将无法获取剩余的 url,因此爬虫将停止。
解决方法也很简单,就是直接将id_list中的数据存入开头的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()