昨天用 dotnet core 把在 python 下实现的爬虫重写了一遍,体验很好( Linq + HtmlAgilityPack + ScrapySharp ),做一些零碎的记录

轮子的选择

以下涉及到的库均可以使用 Nuget 安装

HTML 爬取库

如果遇到没有 DDOS 防护的静态页面,在 python 下用 urllib 库发送请求即可获取网页代码

// ...
req = request.Request(url, headers=HEAD)
response = request.urlopen(req)
html = response.read().decode('utf-8')
// ...

在 dotnet 下同样可以使用自带的 WebClient、WebRequest 实现,但既然使用了 ScrapySharp 这个解析库(原因见下文),这里也可以使用 ScrapySharp 自带的 Web Client 实现

using ScrapySharp.Network;
// ...
var browser = new ScrapingBrowser();
WebPage page = browser.NavigateToPage(new Uri("your url here"));
var html = page.Content;
// ...

除此之外,python 下万能的浏览器自动化工具 selenium 也同样支持 dotnet core,区别在于一些 Options 的添加

在 python 下(使用 Chrome)

chrome_options = Options()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
prefs = {'profile.managed_default_content_settings.images': 2} # 不加载图片
chrome_options.add_experimental_option('prefs', prefs)
browser = webdriver.Chrome(options=chrome_options)

在 dotnet core 下,同样的打开方式变成了

var chromeOptions = new ChromeOptions();
chromeOptions.AddArgument("--no-sandbox");
chromeOptions.AddArgument("--disable-dev-shm-usage");
chromeOptions.AddUserProfilePreference("profile.managed_default_content_settings.images", 2); //这里
var mychrome = new ChromeDriver(chromeOptions);

HTML 解析库

对于 python 下常用的 html 解析库 BeautifulSoup4,在 dotnet core 下我选择了 HtmlAgilityPack

在 python 下,我通常使用以下方法将获取到的 html 源代码解析为 lxml (或其他)格式

# ...
soup = BeautifulSoup(html, 'lxml')
# ...

现在在 dotnet core 下,变成了

using HtmlAgilityPack;

// ...
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
///
htmlDoc.Load("/your/path/here/pageSource.html");
从文件中加载
///
// ...

HAP 默认使用 XPath 语法(在 XML 文档中查找信息的语言),且暂不支持 CSS 选择器

例如在 python 中,我通常以下面的手法筛选信息

# ...
result = soup.select("div.xxxclass")
# ...

因为懒得学习 XPath想尽快尝试 dotnet core 的爬虫,ScrapySharp 出现了,此框架是对 HtmlAgilityPack 的一次扩展,添加了其缺少的 CSS 选择器,并且自带一个 Web Client 用来获取网页代码

安装之后就可以使用 CssSelect 方法筛选网页代码

using ScrapySharp.Extensions;
// ...
var result = htmlDoc.DocumentNode.CssSelect("table tr.app");
// ...

稍微熟悉 XPath 语法后,也可以很方便提取信息,如下(单个节点中的信息):

using HtmlAgilityPack;
// ...
string subID = tds[1].SelectSingleNode(".//a[@href]").Attributes["href"].Value.Split('/')[2];
string name = tds[1].SelectSingleNode(".//b").InnerText;
string URL = tds[0].SelectSingleNode(".//a[@href]").Attributes["href"].Value.Split('?')[0];
// ...

Continue reading “从 Python 向 Dotnet Core 的爬虫迁移记录”

Web 界面较硬核,可自行魔改

原作者为 Marcelo Rovai · Github

环境

硬件

  • Raspberry 3
    • 之后的版本都可以
  • 树莓派 CSI 接口的摄像头
  • 显示器
    • 可选,我这里接了官方七寸 LCD 屏

软件

  • Raspbian Buster
  • python 3.7.7
    • flask
    • 这两个 buster 自带,应该不用手动安装

参考

步骤

  1. 安装摄像头(详见 简易树莓派拍照远程监控,为了方便阅读复读一次)
    • 排线蓝色一面朝向以太网接口,插反不会 boom 但无法识别
    • 不能热插拔
    • 摄像头板子上有 led 灯(拍照的时候会亮),注意不要用导电的材料连通,否则灯会坏(反而更隐蔽?),我当时为了省事把摄像头卡在两根 GPIO 针中间,某天碰了一下,一个火花闪过LED再也没亮过
  2. 测试摄像头
    • 执行指令,拍张照先
      raspistill -o ~/Desktop/image.jpg


      这块没被打码的湿巾桶会作为之后成功运行的证据

  3. 建立 flask 项目结构
    • 新建 static 目录(flask 会从该目录加载样式表和 js)
    • 在 static 目录新建样式表 style.css
      body{
          background: blue;
          color: yellow;
          padding:1%;
          text-align: center;
      }

      想美观一点就改一下配色

    • 新建与 static 目录同级的 templates 目录(flask 会从该目录加载 html 模板)
    • 在 templates 目录下新建 index.html
      <html>
        <head>
          <title>MJRoBot Lab Live Streaming</title>
          <link rel="stylesheet" href='../static/style.css'/>
        </head>
        <body>
          <h1>MJRoBot Lab Live Streaming</h1>
          <h3><img src="{{ url_for('video_feed') }}" width="50%"></h3>
          <hr>
          <p> @2018 Developed by MJRoBot.org</p>
        </body>
      </html>
  4. flask 代码
    • 源代码在参考链接中(作者不是我),这里复读一遍
      • 注意:如果选择下载作者的代码,请使用 camWebServer 目录下的代码,camWebServer2 中添加了温湿度传感器 DHT22 的代码,如果没有连接 DHT22 会报错
    • 在 static 的同级目录下新建 app.py
      #!/usr/bin/env python
      # -*- coding: utf-8 -*-
      #
      #   appCam.py
      #   based on tutorial ==> https://blog.miguelgrinberg.com/post/video-streaming-with-flask
      #   PiCam Local Web Server with Flask
      # MJRoBot.org 19Jan18
      
      from flask import Flask, render_template, Response
      
      # Raspberry Pi camera module (requires picamera package)
      from camera_pi import Camera
      
      app = Flask(__name__)
      
      
      @app.route('/')
      def index():
          """Video streaming home page."""
          return render_template('index.html')
      
      
      def gen(camera):
          """Video streaming generator function."""
          while True:
              frame = camera.get_frame()
              yield (b'--frame\r\n'
                     b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
      
      
      @app.route('/video_feed')
      def video_feed():
          """Video streaming route. Put this in the src attribute of an img tag."""
          return Response(gen(Camera()),
                          mimetype='multipart/x-mixed-replace; boundary=frame')
      
      
      if __name__ == '__main__':
          app.run(host='0.0.0.0', port =80, debug=True, threaded=True)
    • 在 static 的同级目录下新建 camera_pi.py
      #!/usr/bin/env python
      # -*- coding: utf-8 -*-
      #
      #  camera_pi.py
      #  
      #  
      #  
      import time
      import io
      import threading
      import picamera
      
      
      class Camera(object):
          thread = None  # background thread that reads frames from camera
          frame = None  # current frame is stored here by background thread
          last_access = 0  # time of last client access to the camera
      
          def initialize(self):
              if Camera.thread is None:
                  # start background frame thread
                  Camera.thread = threading.Thread(target=self._thread)
                  Camera.thread.start()
      
                  # wait until frames start to be available
                  while self.frame is None:
                      time.sleep(0)
      
          def get_frame(self):
              Camera.last_access = time.time()
              self.initialize()
              return self.frame
      
          @classmethod
          def _thread(cls):
              with picamera.PiCamera() as camera:
                  # camera setup
                  camera.resolution = (320, 240)
                  camera.hflip = True
                  camera.vflip = True
      
                  # let camera warm up
                  camera.start_preview()
                  time.sleep(2)
      
                  stream = io.BytesIO()
                  for foo in camera.capture_continuous(stream, 'jpeg',
                                                       use_video_port=True):
                      # store frame
                      stream.seek(0)
                      cls.frame = stream.read()
      
                      # reset stream for next frame
                      stream.seek(0)
                      stream.truncate()
      
                      # if there hasn't been any clients asking for frames in
                      # the last 10 seconds stop the thread
                      if time.time() - cls.last_access > 10:
                          break
              cls.thread = None
  5. 运行 flask
    • 执行指令
      sudo python3 app.py

      注意不是 root 用户需要 sudo,不然权限不够无法监听 Web 端口

Continue reading “树莓派 CSI 摄像头 + Flask 实现轻量 Web 直播串流”

在写 Steam +1 Telegram Bot 过程中遇到的一些问题

selenium 环境的配置

  • Linux 下参见 Linux配置python selenium + Google Chrome环境
  • macOS与 Linux 大同小异,chromedriver 放到 /usr/local/bin 目录下,如果安装了 Homebrew 则可以通过执行指令
    brew cask install google-chrome

    来安装 Chrome 浏览器

  • Windows 下的 chromedriver 需要放到 PATH 路径下,PATH 路径可以通过右键 【我的电脑】进入【属性 – 高级系统设置 – 环境变量 – 双击 PATH – 新建】来设置

运行 selenium 报错(selenium options)

Linux配置python selenium + Google Chrome环境 中出现过这个问题,长时间不用又忘了

问题已经解决,没有保存具体的报错信息,只能叙述一下引发原因:

开发环境的可以使用 Chrome GUI,将代码部署到服务器(CLI 环境)后,因无法开启 chromedriver 导致代码无法正常运行

因为服务器没有安装 GUI 环境,需要在启动 Chrome 前传入 –headless 参数:

chrome_options = Options()
chrome_options.add_argument('--headless')
browser = webdriver.Chrome(options=chrome_options)

如果使用 root 用户运行程序,还需要加入 –no-sandbox 参数:

chrome_options.add_argument('--no-sandbox')

如果服务器没有GPU,则加入 –disable-gpu 参数:

chrome_options.add_argument('--disable-gpu')

注意,访问有些网站时,使用无头(headless)模式无法获取正常的网页内容,会被判定为客户端有风险

此时只能装一个 GUI 环境,或者发布到 Win Server 上,实测 Win Server (计划任务下 py、pyw 均可以)正常运行,Linux GUI 未测试

加快爬取速度

Update 2020.4.1:

可以通过不加载图片的方式来加快页面加载速度,达到加快爬取速度的效果

实现方法还是添加 chrome options:

'''...'''
chrome_options = Options()
prefs = {'profile.managed_default_content_settings.images': 2}
chrome_options.add_experimental_option('prefs', prefs)
'''...'''

selenium 配合 BeautifulSoup 筛选数据

在一些情况下,需要 selenium 获取动态加载数据或者有防止DDOS(等待x秒)的页面源代码,然后使用 bs 筛选数据,此时可以如下处理:

browser.get(URL)
html = browser.page_source
browser.close()
soup = BeautifulSoup(html, "lxml")

这样之后就可以抛弃 selenium 使用 BeautifulSoup 筛选信息