需求
首先明确需求:爬取网易新闻新闻的标题和新闻详情页的内容
注意项:
- 先从首页获取对应的详情页的url
- 每一个模块的新闻都是动态加载出来的,可配合selenium实现动态加载
- 详情页面的爬取
实现过程
创建工程:scrapy startproject wangyiPro
转入工程:cd wangyiPro
创建爬虫文件:scrapy genspider wangyi www.xxx.com
实现过程
1.1 观察首页结构,定位模块
观察网易新闻的首页,配套开发者工具,定位到需要爬取的模块,编写parse方法获取url
wangyi.py
from selenium import webdriver
from wangyiPro.items import WangyiproItem
import scrapy
# 需求,爬取五大板块下的新闻内容和标题,内容在新闻详情页面中,标题在对应模块下
# 各个模块下的内容是 动态加载的 可配合selenium实现动态加载
# 详情页面的爬取
class WangyiSpider(scrapy.Spider):
name = 'wangyi'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://news.163.com/']
model_urls = []
def parse(self, response):
# 获取不同模块下的url,放入model_urls中
li_list = response.xpath('//div[@class="ns_area list"]/ul/li')
url_list = [2, 3, 5, 6, 10]
for index in url_list:
# 不同模块下对应的url
url = li_list[index]
model_url = url.xpath('./a/@href').extract_first()
self.model_urls.append(model_url)
# 对不同模块下的url进行请求
yield scrapy.Request(model_url, callback=self.parse_model)
1.2 selenium实现动态加载
对不同模块的url发起请求后,不难发现新闻有个明显的加载,这个动态加载的表现,为获取到动态加载的数据,可配合selenium使用。
先在 wangyi.py文件中实例化一个浏览器对象
wangyi.py
# 实例化一个浏览器对象
def __init__(self):
# from selenium import webdriver
self.options = webdriver.ChromeOptions()
# 添加“取消侦听提示”的配置项
self.options.add_experimental_option('excludeSwitches', ['enable-logging'])
self.driver = webdriver.Chrome(executable_path=r"C:\Users\vidfu\PycharmProjects\wangyiPro\chromedriver.exe", options=self.options)
之后,在middlewares.py文件的WangyiproDownloaderMiddleware
类中的process_response
方法中编写代码
middlewares.py
# 拦截5个请求的响应体,进行篡改
def process_response(self, request, response, spider):
# spider 是爬虫对象
# response 是响应对象
# 实例化一个新的响应对象(符合所有需求,包括动态加载出数据),代替原来的响应对象
# 问题-如何动态加载数据?:基于selenium获取动态加载的数据
# 通过url指定request,request指定对应的response
# 获取在爬虫类中定义的浏览器驱动对象
bro = spider.driver
# 拦截到的响应在5给模块中,就换一个新的响应体
if request.url in spider.model_urls:
# 获取请求
bro.get(request.url)
sleep(5)
# 获取到的数据中包含了动态加载
page_text = bro.page_source
# from scrapy.http import HtmlRespon
# 新的响应体
new_response = HtmlResponse(url=request.url,
body=page_text,
encoding='utf-8',
request=request)
return new_response
else:
return response
1.3 发起请求,获取详情页内容
对1.1获取到的模块url发起请求,在items.py
文件中设置字段,获取新闻标题和新闻内容,最后关闭驱动
wangyi.py
# 对不同模块url进行请求, 不同板块的新闻内容是动态加载的
def parse_model(self, response):
div_list = response.xpath('//div[@class="ndi_main"]/div')
for div in div_list:
# 实例化item对象
item = WangyiproItem()
item['title'] = div.xpath('./div/div[1]/h3/a/text()').extract_first()
detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()
yield scrapy.Request(detail_url, callback=self.parse_detail, meta={'item': item})
# 对详情页面进行解析
def parse_detail(self, response):
item = response.meta['item']
item['content'] = ''.join(response.xpath('//div[@class="post_body"]//text()').extract())
yield item
# 关闭开启的驱动
def colse(self, spider):
self.driver.quit()
1.4 设置配置文件
设置User-Agent、robots协议、日志等级、开启管道和中间件,要看一下爬取的内容,在管道类WangyiproPipeline
打印出来
setting.py
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36 Edg/96.0.1054.43'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'
DOWNLOADER_MIDDLEWARES = {
'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
}
ITEM_PIPELINES = {
'wangyiPro.pipelines.WangyiproPipeline': 300,
}
结果展示
遇到一个问题,虽然有打印输出,但是在打印之前会提示”typeerror: request url must be str or unicode, got nonetype“,我在想是我详情页面的url类型出错了吗?,我检查了一下,没检查出个所以然来,没影响输出就不管了,如果有大佬知道是怎么回事,欢迎评论留言。