如何使用Scrapy爬取网站新闻索引页的完整文章正文
问题原因
- 你写的
text': news.css('p.categoryArticle__excerpt::text').get()选择器抓不到文本,是因为你贴的HTML结构里,<p class="categoryArticle__excerpt"></p>是空标签,后面那段预览文字是直接放在父容器div.categoryArticle__content下、和p标签平级的文本节点,根本不属于这个p标签的内容,自然选不到。 - 就算你改选择器拿到了这段文字,也只是列表页展示的几十字截断摘要,不是完整新闻正文,要抓全文必须进单篇新闻的详情页。
- 原代码还有两个隐藏bug:一是
category字段的XPath用//开头是全局匹配,放在循环里每次都拿同一个值,完全没必要;二是下一页直接选a.num会匹配到所有数字页码按钮,容易重复爬取、跳错页甚至死循环。
调整方案
按Scrapy的跨页爬取逻辑改即可:
- 列表页循环里先提取已经能稳定拿到的发布时间、分类、标题,同时提取每条新闻对应的详情页链接
- 用
response.follow跳转到详情页,把已经提取到的字段通过meta参数传递过去 - 新增详情页解析方法,定位详情页的正文容器,提取所有段落文本拼接成完整正文
- 修正下一页的选择规则,只匹配专门的“下一页”按钮,避免跳页错误
修正后可直接运行的代码
import scrapy class CoalNewsFromOilPrice(scrapy.Spider): name = 'coalnews' start_urls = ['https://oilprice.com/Energy/Coal/'] def parse(self, response): # 分类是列表页全局字段,循环外只取一次 category = response.xpath('//h1[@class="categoryHeading"]/text()').get().replace('/', '').replace(' ','') for news in response.css('div.categoryArticle__content'): item = { 'datetime': news.css('p.categoryArticle__meta::text').get(), 'category': category, 'title': news.css('h2.categoryArticle__title::text').get(), } # 提取详情页地址:标题h2的父节点a标签就是详情页入口 detail_url = news.css('h2.categoryArticle__title').xpath('../@href').get() # 跳转详情页,传递已提取的元数据 yield response.follow( detail_url, callback=self.parse_detail, meta={'item': item} ) # 匹配下一页按钮,不存在则停止爬取 next_page = response.css('a.pagination__next::attr(href)').get() if next_page: yield response.follow(next_page, callback=self.parse) def parse_detail(self, response): item = response.meta['item'] # 提取正文所有段落,去空白后拼接 content_paragraphs = response.css('div.article__content p::text').getall() item['text'] = '\n'.join([p.strip() for p in content_paragraphs if p.strip()]) yield item
补充说明
- 如果运行后发现正文抓空,打开浏览器F12检查下详情页正文容器的class名是否有变动,替换成最新的class选择器即可。
- 如果你需要保留列表页的那段短摘要,可以在列表页的item里加一行
'short_excerpt': news.xpath('./text()').getall()[-1].strip(),就能拿到那段游离在p标签外的预览文本。
内容的提问来源于stack exchange,提问作者yangyang
相关产品推荐
相关产品推荐

