어떻게 scrapy 에서 selenium 을 통합 하여 웹 페이지 를 오 르 는 방법 입 니까?
19938 단어 scrapyselenium홈 페이지 를 기어오르다
4.567917.우 리 는 웹 페이지 를 오 를 때 보통 세 개의 파충류 창고 에 사용 된다.requests,scrapy,selenium.requests 는 보통 소형 파충류 에 사용 되 며,scrapy 는 큰 파충류 프로젝트 를 구축 하 는 데 사용 되 며,selenium 은 주로 담당 페이지(복잡 한 js 렌 더 링 페이지,구조 가 매우 어렵 거나 구조 방식 이 자주 변화 함)에 대응 합 니 다4.567917.우리 가 대형 파충류 프로젝트 에 직면 했 을 때 반드시 scrapy 프레임 워 크 를 선택 하여 개발 할 것 이다.그러나 복잡 한 JS 렌 더 링 페이지 를 분석 할 때 매우 번거롭다.비록 selenium 브 라 우 저 렌 더 링 을 사용 하여 이러한 페이지 를 캡 처 하 는 것 이 편리 하지만 이런 방식 에서 우 리 는 페이지 배경 에 어떤 요청 이 발생 했 는 지 에 관심 을 가 질 필요 가 없고 전체 페이지 의 렌 더 링 과정 을 분석 할 필요 가 없다.우 리 는 페이지 의 최종 결과 에 만 관심 을 가 져 야 한다.이 를 통 해 알 수 있 듯 이 올 라 갈 수 있 지만 selenium 의 효율 은 너무 낮다4.567917.그래서 만약 에 scrapy 에서 selenium 을 통합 하여 selenium 에 게 복잡 한 페이지 의 기어 오 르 는 것 을 책임 지게 할 수 있다 면 이런 파충 류 는 무적 이 고 모든 사 이 트 를 기어 올 라 갈 수 있다.
2.환경
chromedriver(환경 변 수 를 설정)
3.1.request 요청 의 절 차 를 분석 합 니 다.
우선 scrapy 의 최신 구조 도 를 살 펴 보 자.
부분 흐름:
첫째:파충류 엔진 생 성 requests 요청,scheduler 스케줄 러 모듈 로 보 내 고 대기 대기 열 에 들 어가 스케줄 을 기다 리 고 있 습 니 다.
두 번 째:scheduler 모듈 은 이러한 requests 를 스케줄 링 하고 팀 을 나 와 파충류 엔진 으로 보 내기 시작 했다.
셋째,파충류 엔진 은 이러한 requests 를 다운로드 미들웨어(여러 개,예 를 들 어 header,대리,사용자 정의 등)로 보 내 처리 합 니 다.
넷 째:처리 후 다운 로 더 모듈 로 보 내 다운로드 합 니 다.이 처리 과정 을 보면 돌파 구 는 미들웨어 부분 을 다운로드 하고 selenium 으로 request 요청 을 직접 처리 합 니 다.
3.2.requests 와 response 중간 처리 부품 소스 코드 분석
관련 코드 위치:
원본 분석:
# :E:\Miniconda\Lib\site-packages\scrapy\core\downloader\middleware.py
"""
Downloader Middleware manager
See documentation in docs/topics/downloader-middleware.rst
"""
import six
from twisted.internet import defer
from scrapy.http import Request, Response
from scrapy.middleware import MiddlewareManager
from scrapy.utils.defer import mustbe_deferred
from scrapy.utils.conf import build_component_list
class DownloaderMiddlewareManager(MiddlewareManager):
component_name = 'downloader middleware'
@classmethod
def _get_mwlist_from_settings(cls, settings):
# settings.py custom_setting Middleware
'''
'DOWNLOADER_MIDDLEWARES': {
'mySpider.middlewares.ProxiesMiddleware': 400,
# SeleniumMiddleware
'mySpider.middlewares.SeleniumMiddleware': 543,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
},
'''
return build_component_list(
settings.getwithbase('DOWNLOADER_MIDDLEWARES'))
# Middleware methods
def _add_middleware(self, mw):
if hasattr(mw, 'process_request'):
self.methods['process_request'].append(mw.process_request)
if hasattr(mw, 'process_response'):
self.methods['process_response'].insert(0, mw.process_response)
if hasattr(mw, 'process_exception'):
self.methods['process_exception'].insert(0, mw.process_exception)
#
def download(self, download_func, request, spider):
@defer.inlineCallbacks
def process_request(request):
# request , Middleware process_request , list
for method in self.methods['process_request']:
response = yield method(request=request, spider=spider)
assert response is None or isinstance(response, (Response, Request)), \
'Middleware %s.process_request must return None, Response or Request, got %s' % \
(six.get_method_self(method).__class__.__name__, response.__class__.__name__)
#
# Middleware process_request , response
# response return , , process_request
# header,proxy , user-agent, proxy, return
# : return Response
# HtmlResponse Response
if response:
defer.returnValue(response)
# process_request , Response
# , Request download_func, , Response
# Middleware process_response ,
defer.returnValue((yield download_func(request=request,spider=spider)))
@defer.inlineCallbacks
def process_response(response):
assert response is not None, 'Received None in process_response'
if isinstance(response, Request):
defer.returnValue(response)
for method in self.methods['process_response']:
response = yield method(request=request, response=response,
spider=spider)
assert isinstance(response, (Response, Request)), \
'Middleware %s.process_response must return Response or Request, got %s' % \
(six.get_method_self(method).__class__.__name__, type(response))
if isinstance(response, Request):
defer.returnValue(response)
defer.returnValue(response)
@defer.inlineCallbacks
def process_exception(_failure):
exception = _failure.value
for method in self.methods['process_exception']:
response = yield method(request=request, exception=exception,
spider=spider)
assert response is None or isinstance(response, (Response, Request)), \
'Middleware %s.process_exception must return None, Response or Request, got %s' % \
(six.get_method_self(method).__class__.__name__, type(response))
if response:
defer.returnValue(response)
defer.returnValue(_failure)
deferred = mustbe_deferred(process_request, request)
deferred.addErrback(process_exception)
deferred.addCallback(process_response)
return deferred
4.코드settings.py 에서 selenium 인 자 를 설정 합 니 다:
# settings.py
# ----------- selenium -------------
SELENIUM_TIMEOUT = 25 # selenium ,
LOAD_IMAGE = True #
WINDOW_HEIGHT = 900 #
WINDOW_WIDTH = 900
spider 에서 request 를 생 성 할 때 어떤 요청 을 selenium 으로 다운로드 해 야 하 는 지 표시 합 니 다.
# mySpider.py
class mySpider(CrawlSpider):
name = "mySpiderAmazon"
allowed_domains = ['amazon.com']
custom_settings = {
'LOG_LEVEL':'INFO',
'DOWNLOAD_DELAY': 0,
'COOKIES_ENABLED': False, # enabled by default
'DOWNLOADER_MIDDLEWARES': {
#
'mySpider.middlewares.ProxiesMiddleware': 400,
# SeleniumMiddleware
'mySpider.middlewares.SeleniumMiddleware': 543,
# scrapy user-agent
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
},
#..................... .......................
# request , selenium , meta
yield Request(
url = "https://www.amazon.com/",
meta = {'usedSelenium': True, 'dont_redirect': True},
callback = self.parseIndexPage,
errback = self.error
)
중간 부품 middlewares.py 를 다운로드 할 때 selenium 으로 페이지 캡 처(핵심 부분)
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from scrapy.http import HtmlResponse
from logging import getLogger
import time
class SeleniumMiddleware():
# pipeline settings , scrapy.crawler.Crawler.settings
@classmethod
def from_crawler(cls, crawler):
# settings.py , selenium ,
return cls(timeout=crawler.settings.get('SELENIUM_TIMEOUT'),
isLoadImage=crawler.settings.get('LOAD_IMAGE'),
windowHeight=crawler.settings.get('WINDOW_HEIGHT'),
windowWidth=crawler.settings.get('WINDOW_WIDTH')
)
def __init__(self, timeout=30, isLoadImage=True, windowHeight=None, windowWidth=None):
self.logger = getLogger(__name__)
self.timeout = timeout
self.isLoadImage = isLoadImage
# browser, , chrome
# , Request browser
self.browser = webdriver.Chrome()
if windowHeight and windowWidth:
self.browser.set_window_size(900, 900)
self.browser.set_page_load_timeout(self.timeout) #
self.wait = WebDriverWait(self.browser, 25) #
def process_request(self, request, spider):
'''
chrome
:param request: Request
:param spider: Spider
:return: HtmlResponse
'''
# self.logger.debug('chrome is getting page')
print(f"chrome is getting page")
# meta , selenium
usedSelenium = request.meta.get('usedSelenium', False)
if usedSelenium:
try:
self.browser.get(request.url)
#
input = self.wait.until(
EC.presence_of_element_located((By.XPATH, "//div[@class='nav-search-field ']/input"))
)
time.sleep(2)
input.clear()
input.send_keys("iphone 7s")
# enter ,
input.send_keys(Keys.RETURN)
#
searchRes = self.wait.until(
EC.presence_of_element_located((By.XPATH, "//div[@id='resultsCol']"))
)
except Exception as e:
# self.logger.debug(f'chrome getting page error, Exception = {e}')
print(f"chrome getting page error, Exception = {e}")
return HtmlResponse(url=request.url, status=500, request=request)
else:
time.sleep(3)
return HtmlResponse(url=request.url,
body=self.browser.page_source,
request=request,
#
encoding='utf-8',
status=200)
5.실행 결과6.존재 하 는 문제점
6.1.Spider 가 닫 혔 습 니 다.chrome 은 종료 되 지 않 았 습 니 다.
2018-04-04 09:26:18 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/response_bytes': 2092766,
'downloader/response_count': 2,
'downloader/response_status_count/200': 2,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2018, 4, 4, 1, 26, 16, 763602),
'log_count/INFO': 7,
'request_depth_max': 1,
'response_received_count': 2,
'scheduler/dequeued': 2,
'scheduler/dequeued/memory': 2,
'scheduler/enqueued': 2,
'scheduler/enqueued/memory': 2,
'start_time': datetime.datetime(2018, 4, 4, 1, 25, 48, 301602)}
2018-04-04 09:26:18 [scrapy.core.engine] INFO: Spider closed (finished)
위,우 리 는 브 라 우 저 대상 을 Middleware 미들웨어 미들웨어 에 넣 었 습 니 다.process 만 할 수 있 습 니 다.request 와 processresponse,중간 부품 에서 scrapy 를 어떻게 호출 하 는 close 방법 을 소개 하지 않 았 습 니 다.
솔 루 션:신 호 량 을 이용 하여 spider 를 받 으 면closed 신호 시 browser.quit()호출
6.2.한 프로젝트 가 여러 개의 spider 를 동시에 시작 하면 Middleware 의 selenium 을 함께 사용 하여 동시 다발 에 불리 합 니 다.
scrapy+selenium 방식 으로 일부,심지어 일부 페이지 만 chrome 을 사용 하기 때 문 입 니 다.chrome 을 Middleware 에 넣 는 데 이렇게 많은 제한 이 있 는데 왜 chrome 을 spider 에 넣 을 수 없 습 니까?이러한 장점 은 모든 spider 는 자신의 chrome 을 가지 고 있 습 니 다.이렇게 여러 개의 spider 를 시작 할 때 여러 개의 chrome 이 있 습 니 다.모든 spider 가 하나의 chrome 을 공유 하 는 것 이 아니 라 우리 의 병발 에 좋 습 니 다.
솔 루 션:chrome 의 초기 화 를 spider 에 넣 고 모든 spider 가 자신의 chrome 을 독점 합 니 다.
7.개정판 코드
settings.py 에서 selenium 인 자 를 설정 합 니 다:
# settings.py
# ----------- selenium -------------
SELENIUM_TIMEOUT = 25 # selenium ,
LOAD_IMAGE = True #
WINDOW_HEIGHT = 900 #
WINDOW_WIDTH = 900
spider 에서 request 를 생 성 할 때 어떤 요청 을 selenium 으로 다운로드 해 야 하 는 지 표시 합 니 다.
# mySpider.py
# selenium
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
# scrapy
from scrapy.utils.project import get_project_settings
# , ,
# from scrapy.xlib.pydispatch import dispatcher
from scrapy import signals
# scrapy
from pydispatch import dispatcher
class mySpider(CrawlSpider):
name = "mySpiderAmazon"
allowed_domains = ['amazon.com']
custom_settings = {
'LOG_LEVEL':'INFO',
'DOWNLOAD_DELAY': 0,
'COOKIES_ENABLED': False, # enabled by default
'DOWNLOADER_MIDDLEWARES': {
#
'mySpider.middlewares.ProxiesMiddleware': 400,
# SeleniumMiddleware
'mySpider.middlewares.SeleniumMiddleware': 543,
# scrapy user-agent
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
},
# chrome spider , spider
def __init__(self, timeout=30, isLoadImage=True, windowHeight=None, windowWidth=None):
# settings.py
self.mySetting = get_project_settings()
self.timeout = self.mySetting['SELENIUM_TIMEOUT']
self.isLoadImage = self.mySetting['LOAD_IMAGE']
self.windowHeight = self.mySetting['WINDOW_HEIGHT']
self.windowWidth = self.mySetting['windowWidth']
# chrome
self.browser = webdriver.Chrome()
if self.windowHeight and self.windowWidth:
self.browser.set_window_size(900, 900)
self.browser.set_page_load_timeout(self.timeout) #
self.wait = WebDriverWait(self.browser, 25) #
super(mySpider, self).__init__()
# , spider_closed , mySpiderCloseHandle , chrome
dispatcher.connect(receiver = self.mySpiderCloseHandle,
signal = signals.spider_closed
)
# : chrome
def mySpiderCloseHandle(self, spider):
print(f"mySpiderCloseHandle: enter ")
self.browser.quit()
#..................... .......................
# request , selenium , meta
yield Request(
url = "https://www.amazon.com/",
meta = {'usedSelenium': True, 'dont_redirect': True},
callback = self.parseIndexPage,
errback = self.error
)
미들웨어 를 다운로드 할 때 selenium 으로 페이지 를 캡 처 합 니 다.
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from scrapy.http import HtmlResponse
from logging import getLogger
import time
class SeleniumMiddleware():
# Middleware spider, spider , __init__ chrome
def process_request(self, request, spider):
'''
chrome
:param request: Request
:param spider: Spider
:return: HtmlResponse
'''
print(f"chrome is getting page")
# meta , selenium
usedSelenium = request.meta.get('usedSelenium', False)
if usedSelenium:
try:
spider.browser.get(request.url)
#
input = spider.wait.until(
EC.presence_of_element_located((By.XPATH, "//div[@class='nav-search-field ']/input"))
)
time.sleep(2)
input.clear()
input.send_keys("iphone 7s")
# enter ,
input.send_keys(Keys.RETURN)
#
searchRes = spider.wait.until(
EC.presence_of_element_located((By.XPATH, "//div[@id='resultsCol']"))
)
except Exception as e:
print(f"chrome getting page error, Exception = {e}")
return HtmlResponse(url=request.url, status=500, request=request)
else:
time.sleep(3)
# , Response (HtmlResponse )
return HtmlResponse(url=request.url,
body=spider.browser.page_source,
request=request,
#
encoding='utf-8',
status=200)
실행 결과(spider 종료,mySpiderCloseHandle 실행 으로 chrome 브 라 우 저 닫 기):['categorySelectorAmazon1.pipelines.MongoPipeline']
2018-04-04 11:56:21 [scrapy.core.engine] INFO: Spider opened
2018-04-04 11:56:21 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
chrome is getting page
parseProductDetail url = https://www.amazon.com/, status = 200, meta = {'usedSelenium': True, 'dont_redirect': True, 'download_timeout': 25.0, 'proxy': 'http://H37XPSB6V57VU96D:[email protected]:9020', 'depth': 0}
chrome is getting page
2018-04-04 11:56:54 [scrapy.core.engine] INFO: Closing spider (finished)
mySpiderCloseHandle: enter
2018-04-04 11:56:59 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/response_bytes': 1938619,
'downloader/response_count': 2,
'downloader/response_status_count/200': 2,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2018, 4, 4, 3, 56, 54, 301602),
'log_count/INFO': 7,
'request_depth_max': 1,
'response_received_count': 2,
'scheduler/dequeued': 2,
'scheduler/dequeued/memory': 2,
'scheduler/enqueued': 2,
'scheduler/enqueued/memory': 2,
'start_time': datetime.datetime(2018, 4, 4, 3, 56, 21, 642602)}
2018-04-04 11:56:59 [scrapy.core.engine] INFO: Spider closed (finished)
scrapy 에 selenium 을 통합 하여 웹 페이지 를 오 르 는 방법 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 scrapy 통합 selenium 이 웹 페이지 를 오 르 는 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Web Scraping con scrapy y regexcomo solo tenemos un url se la pasamos directamente a scrapy.Request como string y el callback lo dirigimos a nuestro se...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.