0


爬虫 -- 使用selenium和scrapy爬取BBC、NYTimes、Snopes等网站的内容

源代码:GitHub - QQQQQQBY/CrawlNYTimes: 使用scrapy爬虫爬取纽约时报、BBC、Snopes等网站数据

架构组成

  1. 文件结构:
  1. myproject/
  2. main.py
  3. scrapy.cfg
  4. myproject/
  5. __init__.py
  6. items.py
  7. middlewares.py
  8. pipelines.py
  9. settings.py
  10. spiders/
  11. __init__.py
  12. nytimes.py

  1. scrapy.cfg
  1. 1. scrapy.cfg

是 Scrapy 项目配置文件,它用于定义项目的基本配置信息:

  1. 例如可以进行如下配置,这里的
  1. myproject

是项目的名称,你需要将其替换为你自己的项目名称:

  1. [settings]
  2. default = myproject.settings
  3. [deploy]
  4. #url = http://localhost:6800/
  5. project = myproject

  1. items.py

2.

  1. items.py

是 Scrapy 项目中的一个文件,用于定义数据结构,也称为 "item"。在 Scrapy 中,items 是用来存储从网站提取的数据。每个 item 通常对应于你想从网站上抓取的一个对象,比如一篇文章、一个产品、一条新闻等等

  1. 例如,可以这样写,
  1. scrapy.Field()

是一个特殊的类型,表示这个字段是一个 Scrapy item 字段。"title, date"这些都是你自己定义的变量,将来需要被网站上抓取的内容赋值:

  1. import scrapy
  2. class SnopesItem(scrapy.Item):
  3. title = scrapy.Field()
  4. date = scrapy.Field()
  5. url = scrapy.Field()
  6. claim = scrapy.Field()
  7. rating = scrapy.Field()
  8. site = scrapy.Field()
  9. tag = scrapy.Field()
  1. Scrapy 项目中,当你抓取到数据时,你会创建一个 item 对象,并将抓取到的数据赋值给相应的字段。例如:
  1. import scrapy
  2. from myproject.items import SnopesItem
  3. class NYTimesSpider(scrapy.Spider):
  4. name = "nytimes"
  5. start_urls = ['https://www.nytimes.com/section/politics']
  6. def parse(self, response):
  7. for article in response.css('.css-18yolpw'):
  8. item = SnopesItem()
  9. item['title'] = article.css('h2::text').get()
  10. item['date'] = article.css('.css-1d8a290 span::text').get()
  11. item['url'] = response.urljoin(article.css('a::attr(href)').get())
  12. item['claim'] = article.css('p::text').get()
  13. item['rating'] = "True"
  14. item['site'] = "NYTimes"
  15. item['tag'] = "Politics"
  16. yield item

  1. middlewares.py
    1. middlewares.py

    是 Scrapy 项目中的一个文件,用于定义中间件(middlewares)。中间件是在 Scrapy 中处理请求和响应的钩子(hooks),它们可以用于修改或处理 Scrapy 发出的每一个请求和收到的每一个响应。

  1. 请求预处理:在 Scrapy 发出请求之前,对请求进行修改或处理。例如,添加自定义的请求头、设置代理、处理 cookie 等。

  2. 响应预处理:在 Scrapy 收到响应之后,对响应进行修改或处理。例如,检查响应状态码、处理重定向、过滤不必要的响应等。

  3. 错误处理:在请求或响应过程中捕获并处理错误。例如,处理连接超时、重试失败的请求等。

例如:

  1. from scrapy import signals
  2. from fake_useragent import UserAgent
  3. class MyCustomDownloaderMiddleware:
  4. def __init__(self):
  5. self.ua = UserAgent()
  6. @classmethod
  7. def from_crawler(cls, crawler):
  8. # This method is used by Scrapy to create your spiders.
  9. s = cls()
  10. crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
  11. return s
  12. def process_request(self, request, spider):
  13. # This method is called for each request that goes through the downloader middleware.
  14. # Here we can modify the request (e.g., set custom headers)
  15. request.headers['User-Agent'] = self.ua.random
  16. return None
  17. def process_response(self, request, response, spider):
  18. # This method is called with the response returned from the downloader.
  19. # Here we can modify the response (e.g., filter certain responses)
  20. if response.status != 200:
  21. spider.logger.warning(f"Non-200 response: {response.status} for {request.url}")
  22. return response
  23. def process_exception(self, request, exception, spider):
  24. # This method is called when an exception is raised during the request/response handling.
  25. # Here we can handle exceptions (e.g., retry the request)
  26. spider.logger.error(f"Request failed: {exception} for {request.url}")
  27. return None
  28. def spider_opened(self, spider):
  29. spider.logger.info('Spider opened: %s' % spider.name)
  1. 更详细的配置介绍可以看其他博客,例如scrapy中间件详解_scrapy 中间件-CSDN博客

  1. pipelines.py

4.

  1. pipelines.py

是 Scrapy 项目中的一个文件,用于定义数据管道(Item Pipeline)。数据管道是在 Scrapy 中处理爬取到的数据(items)的组件。通过数据管道,你可以对爬取到的数据进行一系列的处理,例如清洗数据、验证数据、将数据保存到数据库等

  1. 数据清洗:对爬取到的数据进行清洗和规范化处理,例如去除空白字符、格式化日期等。
  2. 数据验证:验证爬取到的数据是否符合预期,例如检查字段是否为空、数值是否在合理范围内等。
  3. 数据存储:将爬取到的数据保存到数据库、文件或其他存储系统中,例如保存到 MySQL 数据库、MongoDB、JSON 文件等。
  1. pipelines.py

文件的示例,展示了如何定义一个简单的数据管道,将爬取到的数据进行清洗并保存到 JSON 文件中:

  1. import json
  2. class JsonWriterPipeline:
  3. def open_spider(self, spider):
  4. # 当爬虫启动时,这个方法会被调用。我们在这里打开文件。
  5. self.file = open('items.json', 'w')
  6. def close_spider(self, spider):
  7. # 当爬虫关闭时,这个方法会被调用。我们在这里关闭文件。
  8. self.file.close()
  9. def process_item(self, item, spider):
  10. # 这个方法会处理每一个从爬虫传递过来的 item。
  11. # 我们在这里将 item 转换为 JSON 格式并写入文件。
  12. item["url"] = item["url"].strip(" \n\t\r")
  13. item["title"] = item["title"].strip().replace("\n", " ").replace("\r", " ").replace("\t", " ").replace(" ", " ")
  14. item["date"] = item["date"].strip(" \n\t\r")
  15. item["claim"] = item["claim"].strip().replace("\n", " ").replace("\r", " ").replace("\t", " ").replace(" ", " ")
  16. item["rating"] = item["rating"].strip(" \n\t\r")
  17. line = json.dumps(dict(item)) + "\n"
  18. self.file.write(line)
  19. return item

  1. settings.py
  1. 5. settings.py

是 Scrapy 项目中的一个配置文件,用于定义和配置项目的各种设置和参数。这些设置包括爬虫的基本配置、并发请求数、下载延迟、用户代理、中间件和数据管道等

  1. 全局配置:定义全局的项目配置,例如项目名称、日志级别等。
  2. 爬虫配置:定义和控制爬虫的行为,例如并发请求数、下载延迟、超时设置等。
  3. 中间件配置:配置下载中间件、爬虫中间件和扩展。
  4. 数据管道配置:配置数据管道,以便对爬取到的数据进行处理和存储。
  5. 其他配置:包括代理设置、用户代理设置、请求头配置、禁止重定向等。
  1. settings.py

配置示例:

  1. # 项目名称
  2. BOT_NAME = 'myproject'
  3. # 定义爬虫模块
  4. SPIDER_MODULES = ['myproject.spiders']
  5. NEWSPIDER_MODULE = 'myproject.spiders'
  6. # 遵守 robots.txt 规则
  7. ROBOTSTXT_OBEY = True
  8. # 并发请求数
  9. CONCURRENT_REQUESTS = 16
  10. # 下载延迟
  11. DOWNLOAD_DELAY = 3
  12. # 禁用 cookies
  13. COOKIES_ENABLED = False
  14. # 默认请求头
  15. DEFAULT_REQUEST_HEADERS = {
  16. 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
  17. 'Accept-Language': 'en',
  18. }
  19. # 启用或禁用扩展
  20. EXTENSIONS = {
  21. 'scrapy.extensions.telnet.TelnetConsole': None,
  22. }
  23. # 启用或禁用下载中间件
  24. DOWNLOADER_MIDDLEWARES = {
  25. 'myproject.middlewares.MyCustomDownloaderMiddleware': 543,
  26. }
  27. # 启用或禁用爬虫中间件
  28. SPIDER_MIDDLEWARES = {
  29. 'myproject.middlewares.MyCustomSpiderMiddleware': 543,
  30. }
  31. # 启用或禁用数据管道
  32. ITEM_PIPELINES = {
  33. 'myproject.pipelines.MyCustomPipeline': 300,
  34. }
  35. # 启用自动限速扩展
  36. AUTOTHROTTLE_ENABLED = True
  37. AUTOTHROTTLE_START_DELAY = 5
  38. AUTOTHROTTLE_MAX_DELAY = 60
  39. AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
  40. AUTOTHROTTLE_DEBUG = False
  41. # 启用 HTTP 缓存
  42. HTTPCACHE_ENABLED = True
  43. HTTPCACHE_EXPIRATION_SECS = 0
  44. HTTPCACHE_DIR = 'httpcache'
  45. HTTPCACHE_IGNORE_HTTP_CODES = []
  46. HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

spiders/nytimes.py

  1. spiders/nytimes.py 文件通常是一个 Scrapy 爬虫脚本,这里的示例用于定义从 New York Times 网站上爬取数据的爬虫。这个文件中会包含爬虫类、爬取逻辑、数据提取规则等。

    1. 这里将selenium包和scrapy包配合使用抓取网络数据,但有些网站是可以只使用scrapy就可以完成数据爬取的。
    2. **PS:** 这是因为NYtimes网站的内容动态加载的,具体可以看https://www.nytimes.com/section/politics,网页向下滚动后加载接下来的内容,因此我们在抓取内容时也需要模拟人类向下滚动页面的操作,这就需要使用selenium包加载页面并滚动到底部,以触发动态内容加载:
  1. # 导入所需的模块和库,包括 Scrapy、Selenium、以及项目中的 SnopesItem 数据结构
  2. import scrapy
  3. from snopes.items import SnopesItem
  4. from fake_useragent import UserAgent
  5. from selenium import webdriver
  6. from selenium.webdriver.chrome.options import Options
  7. from selenium.webdriver.chrome.service import Service
  8. from scrapy.http import HtmlResponse
  9. import time
  10. # 定义爬虫的主要逻辑
  11. class NYTimesSpider(scrapy.Spider):
  12. # 定义爬虫的名称,在命令行运行爬虫时使用
  13. name = "NYTimes"
  14. # 限定爬虫可以访问的域名和起始 URL
  15. allowed_domains = ["nytimes.com"]
  16. start_urls = ["https://www.nytimes.com/section/politics"]
  17. # 使用 fake_useragent 随机生成一个用户代理,以避免被网站屏蔽
  18. ua = UserAgent(browsers=["chrome"])
  19. # 初始化爬虫,包括配置 Selenium 的 ChromeDriver
  20. def __init__(self, *args, **kwargs):
  21. super(NYTimesSpider, self).__init__(*args, **kwargs)
  22. # 配置 Selenium 的 ChromeDriver
  23. chrome_driver_path = "C:/path/to/chromedriver" # chromedriver的路径,具体是你电脑中该执行器的位置
  24. # 设置浏览器选项,如禁用 GPU、无头模式、设置窗口大小等
  25. chrome_options = Options()
  26. chrome_options.add_argument("--headless")
  27. chrome_options.add_argument("--disable-gpu")
  28. chrome_options.add_argument("--no-sandbox")
  29. chrome_options.add_argument("--window-size=1920,1080")
  30. # 初始化 Selenium 的 ChromeDriver 实例
  31. self.service = Service(chrome_driver_path)
  32. # webdriver.Chrome 是 selenium.webdriver 模块中的类,用于启动和控制 Chrome 浏览器
  33. # options=chrome_options 用于传递浏览器配置选项,如禁用 GPU、无头模式、设置窗口大小等
  34. self.driver = webdriver.Chrome(service=self.service, options=chrome_options)
  35. # 负责请求起始 URL 并处理页面加载和滚动,以获取完整的页面内容
  36. def parse(self, response):
  37. # 将 Web 浏览器导航到响应的 URL
  38. self.driver.get(response.url)
  39. time.sleep(10) # 等待页面加载
  40. # 检索网页的当前高度
  41. last_height = self.driver.execute_script("return document.body.scrollHeight")
  42. while True:
  43. # 获取当前页面的HTML内容并传递给Scrapy
  44. page_source = self.driver.page_source
  45. fake_response = HtmlResponse(url=self.driver.current_url, body=page_source, encoding='utf-8') # 使用 HTML 内容创建虚假响应
  46. yield from self.parse_articles(fake_response) # 解析相应中的文章,提取当前页面中需要的信息即item
  47. # 滚动到页面底部(下一页)
  48. self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
  49. time.sleep(5) # 等待新内容加载
  50. # 检索网页的新高度,是last_height 的高度加上新内容的高度
  51. new_height = self.driver.execute_script("return document.body.scrollHeight")
  52. # 检查新高度是否与之前的高度相同,若相同,表示已经加载完毕
  53. if new_height == last_height:
  54. break
  55. last_height = new_height
  56. # 定义处理响应和提取数据的方法
  57. def parse_articles(self, page_source):
  58. # 迭代与 CSS 选择器匹配的每个文章元素
  59. for article in response.css('.css-18yolpw'):
  60. item = SnopesItem()
  61. # css选择器选择需要的内容
  62. item["title"] = article.css('div:nth-child(1) > article:nth-child(1) > a:nth-child(2) > h3:nth-child(1)::text').get()
  63. item["date"] = article.css('div:nth-child(1) > div:nth-child(2) > span:nth-child(1)::text').get()
  64. item["url"] = response.urljoin(article.css('div:nth-child(1) > article:nth-child(1) > a:nth-child(2)::attr(href)').get())
  65. item["claim"] = article.css('div:nth-child(1) > article:nth-child(1) > p:nth-child(3)::text').get()
  66. item["rating"] = "True"
  67. item["site"] = "NYTimes"
  68. item["tag"] = "NYTimes"
  69. # 执行完后是自动调用了pipeline.py 对数据进行清洗保存等
  70. yield item
  1. 只使用scrapy完成数据爬取案例:(点击网页中的下一页按钮,之后继续爬取)
  1. import json
  2. import scrapy
  3. from fake_useragent import UserAgent
  4. from snopes.items import SnopesItem
  5. class SnopesFactSpider(scrapy.Spider):
  6. name = "snopes_fact" # specifies the name of the spider
  7. allowed_domains = ["www.snopes.com"] # domain names that the spider is allowed to crawl
  8. # base_url = 'https://www.snopes.com/tag/joe-biden/?pagenum={}'
  9. base_url = 'https://www.snopes.com/'
  10. start_urls = ["https://www.snopes.com/tag/joe-biden/"] # URLs that the spider will start crawling from
  11. ua = UserAgent()
  12. # def start_requests(self):
  13. # for page in range(2,7):
  14. # url = self.base_url.format(page)
  15. # yield scrapy.Request(url,callback=self.parse)
  16. def parse(self, response): # processing responses and extracting data
  17. # follow links to article pages
  18. for href in response.css(".outer_article_link_wrapper::attr(href)"):
  19. yield response.follow(href, self.parse_article,
  20. headers={"User-Agent": self.ua.random},)
  21. # receives a Response object and should return either Item objects, Request objects, or an iterable of either
  22. # follow pagination links
  23. for href in response.css(".next-button::attr(href)"):
  24. yield response.follow(href, self.parse,
  25. headers={"User-Agent": self.ua.random},)
  26. def parse_article(self, response):
  27. item = SnopesItem()
  28. item["url"] = response.url
  29. item["title"] = response.css(".title-container > h1::text").extract_first("")
  30. try:
  31. item["date"] = json.loads(response.css("script[type='application/ld+json']::text").extract_first())[
  32. "datePublished"]
  33. except:
  34. item["date"] = ""
  35. item["claim"] = response.css(".claim_cont::text").extract_first("")
  36. item["rating"] = response.css(".rating_title_wrap::text").extract_first("")
  37. # item["head_image_url"] = response.css("#cover-main::attr(src)").extract_first("")
  38. # item["body"] = response.css("#fact_check_rating_container ~ *").extract()
  39. # item["sources"] = response.css("#sources_rows > p").extract()
  40. item["site"] = "snopes"
  41. item["tag"] = "joe-biden"
  42. yield item
  43. # pass

main.py

  1. main.py 调用具体的爬虫类

    1. 例如:
  1. # main.py
  2. import sys
  3. import os
  4. from scrapy.cmdline import execute
  5. def start_scrapy():
  6. # sys.path.append(os.path.dirname(os.path.abspath(__file__)))
  7. # sys.path.append("C:/Users/lenovo/Desktop/reddit_new/AgentReddit/snopes/")
  8. os.chdir(os.path.dirname(os.path.abspath(__file__)))
  9. # 运行单个爬虫
  10. execute(["scrapy", "crawl", "NYTimes"])
  11. if __name__ == '__main__':
  12. start_scrapy()

只需要改动"NYTimes"就可以,这是与 spiders/nytimes.py中的"name"相对应。

标签: 爬虫 selenium scrapy

本文转载自: https://blog.csdn.net/qq_40671063/article/details/139442279
版权归原作者 无脑敲代码,bug漫天飞 所有, 如有侵权,请联系我们删除。

“爬虫 -- 使用selenium和scrapy爬取BBC、NYTimes、Snopes等网站的内容”的评论:

还没有评论