Scrapy——Selenium
首先,我需要对Scrapy框架的爬虫文件中一些属性与方法再做进一步的解释。
# 以下代码是scrapy框架中的爬虫文件
import scrapy
class NewsSpider(scrapy.Spider):
name = "news"
allowed_domains = ["news.163.com"]
start_urls = ["https://news.163.com"]
def parse(self, response):
pass
- name:爬虫的名字,可以使用它来运行我们的爬虫程序
- allowed_domains:这个是表明我们被允许访问的域名
- start_urls:这是一个列表,里面表示你需要请求的起始链接,可以存放多个链接内容。
- parse():这是scrapy框架中的自定义的方法,这里面传入了一个response参数,这个参数通常是从start_urls列表中的URL发出的HTTP响应。在Scrapy框架中,parse方法是一个爬虫类中的核心方法,用于解析响应并提取数据或生成新的请求。
接下来,需要对中间件文件做进一步的讲解。
def process_request(self, request, spider):
# Called for each request that goes through the downloader
# middleware.
# Must either:
# - return None: continue processing this request
# - or return a Response object
# - or return a Request object
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
return None
def process_response(self, request, response, spider):
# Called with the response returned from the downloader.
# Must either;
# - return a Response object
# - return a Request object
# - or raise IgnoreRequest
return response
在Scrapy框架中, process_request
和process_response
是中间件中定义的两个不同的方法,它们在请求和响应的处理流程中扮演这不同的角色。
-
process_request
方法是在Scrapy引擎发送请求之前调用的。这个方法可以用来修改即将发生的请求,例如添加或修改HTTP头部、更改请求的URL、设置cookies等。如果需要对请求进行预处理或需要再请求发送到网络上之前做一些额外的处理,那么可以在这个方法中实现这些逻辑。 -
`process_response 方法是在Scrapy引擎接收到响应后调用的。这个方法可以用来修改或处理响应数据,例如检查响应状态码、处理异常情况、提取数据或进一步修改响应内容等。
在使用 selenium 时,我们一般只需要在发送请求之后,使用 process_response 方法来接收响应并对其进行处理。
网易新闻-实战案例
# 中间件文件
class SeleniumNewsMiddleware(object):
@classmethod
def from_crawler(cls,crawler):
middleware = cls()
"""
以下两行代码是在使用Scrapy框架的信号机制。
当某个事件发生时,框架会发送一个信号,我们
可以连接一个处理函数来响应这个信号。
"""
crawler.signals.connect(middleware.spider_opened,signal=signals.spider_opened)
crawler.signals.connect(middleware.spider_closed,signal=signals.spider_closed)
return middleware
"""
对以上代码进行讲解
1、 @classmethod:这是一个Python装饰器,他讲后面的函数转变为一个类方法。
类方法可以通过类名直接调用,而不需要创建类的实例。
2、 def from_crawler(cls, crawler): 这是 from_crawler 方法的定义。cls代表当前的 类。而crawler是传递给这个方法的Scrapy爬虫的实例。这个方法是类的静态方法。
3、 middleware = cls(): 这行代码创建了当前中间件类的一个新的实例。这个实例将包含中间 件的状态和行为。
4、 return middleware: 这个方法返回新创建的中间件实例。这个实例将被Scrapy框架用于后 续的操作,如处理请求和响应。
"""
def spider_opened(self,spider):
options = Options() # 启用配置文件
options.add_argument("--headless") # 启用无头模式
options.add_argument("--disable-gpu") # 将谷歌可视化界面取消
options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 驱动selenium
self.driver = webdriver.Chrome(options=options)
def spider_closed(self,spider):
self.driver.close()
def process_response(self,request,response,spider):
# 对爬虫文件中的链接进行请求
print(request.url)
if request.url in spider.newsUrls:
self.driver.get(url=request.url)
# 下拉
self.driver.execute_script('window.scrollTo(0,document.body.scrollHeight);')
time.sleep(2)
# 获取渲染后的源码
body = self.driver.page_source
# 将 selenium 渲染得到的源码打包给引擎
return HtmlResponse(url=request.url,body=body ,encoding='utf-8' ,request=request)
else:
return response
以上代码选取了两个Scrapy框架中的可选方法,spider_opened 和 spider_closed 两个方法。并使用了crawler.signals.connect内置函数来对这两个方法进行连接,当触发相关信号时,这两个方法会自动进行调用。所以我们在这个 spider_opened 里定义了一个全局的驱动器,然后在 spider_closed 方法中定义了自动关闭驱动器的方法。
我选择了在 process_response 方法中来使用驱动器。首先解释它的三个参数 (我个人的理解):
-
request:这是Scrapy的Request对象,代表了即将被发送的HTTP请求。它包含了请求的URL、方法(如 GET 或 POST)、请求头、body等信息。当爬虫生成一个请求并将其发送到网络上时,这个请求会被传递给所有的下载中间件。 在
process_response
方法中,request
参数就是原始的请求对象。 -
response:在
process_response
方法中,response
参数就是从网络上获取到的原始响应对象。 -
spider:这是爬虫的实例。每个Scrapy爬虫都是 scrapy.Spider 类的一个实例,它定义了爬虫的行为,包括起始URL、如何解析响应、如何提取数据等。spider 参数允许中间件访问爬虫的特定属性和方法。
这三个参数共同为下载中间件提供了足够的信息来处理请求和响应。中间件可以使用这些参数
修改请求、处理响应、生成新的请求、记录数据等。
# 爬虫文件
import scrapy
from scrapy import cmdline
from SpiderNews.items import SpidernewsItem
class NewsSpider(scrapy.Spider):
name = "news"
allowed_domains = ["163.com"]
start_urls = ["https://news.163.com"]
def __init__(self):
self.newsUrls = [] # 将获取的新的链接存放在这里面
def parse(self, response):
# 定义存放 names 的列表、
newsNames = []
# 从这个方法中进一步获取 href
href_selectorList = response.xpath("//div[@class='ns_area list']//li")
"""
以上获得的链接仍然是一个 SelectorList 对象
需要进一步处理
"""
# 定义索引
index = [1,2,4,5]
for i in index:
new_name = href_selectorList[i].xpath('./a/text()').extract_first()
new_url = href_selectorList[i].xpath('./a/@href').extract_first()
newsNames.append(new_name)
self.newsUrls.append(new_url)
print(new_name)
print(new_url)
for url,name in zip(self.newsUrls,newsNames):
yield scrapy.Request(url=url,callback=self.parseNewUrl,meta={'name':name})
return response
# 定义一个新函数解析新链接
def parseNewUrl(self,response):
# 获取标题和跟帖数量
titleList = response.xpath('//div[@class="news_title"]//a/text()').extract()
replyNumList = response.xpath('//span[@class="post_recommend_tie_icon icons"]/text()').extract()
for title,replynNum in zip(titleList,replyNumList):
item = SpidernewsItem()
item["title"] = title
item["replyNum"] = replynNum
item['name'] = response.meta["name"]
print(title)
yield item
if __name__ == '__main__':
cmdline.execute(['scrapy','crawl','news','--nolog'])
# 管道文件
import json
class SpidernewsPipeline:
def __init__(self):
self.file = open('网易新闻.json','w',encoding='utf-8')
def process_item(self, item, spider):
print(item)
data = dict(item)
json_data = json.dumps(data,ensure_ascii=False) +',\n'
self.file.write(json_data)
return item
def spider_closed(self):
self.file.close()