爬虫实战

环境准备

我们完全手动创建一个Dart工程还是略显麻烦,因此我们需要安装一个脚手架,自动生成一个合乎规范的Dart工程项目,执行以下命令安装stagehand(过时,参考升级到Dart 2.14)

pub global activate stagehand

完成安装后直接使用stagehand命令:stagehand -h可能会报找不到错误,这时候我们有两种办法解决

  1. 配置环境变量

    打开cmd命令行,输入如下命令

     echo %APPDATA%\Pub\Cache\bin
    

    这时可以看到,命令行输出了stagehand命令所在的路径,只需要将该路径加入到系统的Path环境变量即可

  2. 使用pub工具调用

    除了配置环境变量,还可以使用pub global run去调用,由于我本机配置了各种各样的开发语言和工具,命令实在太多,我已经不太喜欢配置环境变量,这里就先使用该方式演示。执行以下命令可以查看一下帮助

     pub global run stagehand -h
    

创建工程

新建一个文件夹spidercd到该目录下,运行以下命令,会在spider下自动生成一个命令行项目

pub global run stagehand console-full

使用vscode打开该项目目录

编辑配置文件pubspec.yaml,避免不必要的下载,删除默认添加的test库依赖,配置如下依赖库

dependencies:
  http: ^0.12.0+2
  html: ^0.14.0+2

这里http库主要用于处理http请求,html库用于处理html内容的解析与提取,它们都是Dart官方提供的非标准库,GitHub链接如下

在项目下执行命令,下载依赖

pub get

数据的获取与解析

网络请求

  // 请求网络数据
  Future<String> _requestData(String url) async {
    var response = await http.get(url, headers: header);
    if (response.statusCode == 200) {
      return response.body;
    }
    return '<html>error! status:${response.statusCode}</html>';
  }

解析标签

  // 解析数据
  List<ItemModel> _htmlParse(String html) {
    Document document = parse(html);

    List contents = <Element>[];
    List images = <Element>[];
    List<Element> parent = document.querySelectorAll('div.j-r-list-c');

    for (var e in parent) {
      var text = e.querySelectorAll("div.j-r-list-c-desc > a");
      var image = e.querySelectorAll("div.j-r-list-c-img > a > img");

      contents.add(text[0]);
      images.add(image.isNotEmpty ? image[0] : Element.tag('img'));
    }

    /// 这里使用css选择器语法提取数据
    // 文本
    // List<Element> contents = document.querySelectorAll(
    //     'div[class="j-r-list"] > ul > li > div > div[class="j-r-list-c-desc"] > a');

    // // 图片
    // List<Element> images = document.querySelectorAll(
    //     'div[class="j-r-list"] > ul > li > div > div[class="j-r-list-c-img"] > a > img');

    // 点赞
    List<Element> likes = document.querySelectorAll(
        'div.j-r-list-tool > div.j-r-list-tool-l > ul > li.j-r-list-tool-l-up > span');
    // 点踩
    List<Element> taps = document.querySelectorAll(
        'div.j-r-list-tool > div.j-r-list-tool-l > ul > li.j-r-list-tool-l-down > span');

    // 头像
    List<Element> avatar =
        document.querySelectorAll('div.j-list-user > div.u-img > a > img');

    // 昵称
    List<Element> nickNames =
        document.querySelectorAll('div.j-list-user > div.u-txt > a');

    // 日期
    List<Element> date =
        document.querySelectorAll('div.j-list-user > div.u-txt > span');

    List data = List<ItemModel>();
    for (var i = 0; i < contents.length; i++) {
      ItemModel model = ItemModel();
      model..avatarUrl = avatar[i].attributes['src']
      ..nickName = nickNames[i].text.trim()
      ..content = contents[i].text.trim()
      ..imgUrl = images[i].attributes['src'] ?? ''
      ..like = int.parse(likes[i].text)
      ..tap = int.parse(taps[i].text)
      ..date = date[i].text;

      data.add(model);
    }
    return data;
  }

这里使用的是css语法选择器进行数据提取。关于css选择器语法,可以参考本人一篇博客 链接地址。另外,在解析时需要注意导包的问题,会存在冲突

import 'package:http/http.dart' as http;
import 'package:html/parser.dart' show parse;
import 'package:html/dom.dart';

数据持久化

本课程分别使用sqlite3PostgreSQL两种数据库演示。若缺乏数据库基础,或不了解sqlite3,可学习本人的一篇博客作为入门 链接地址

在Dart中使用sqlite3需要安装相应的第三方库,这里推荐使用 moor_ffiPostgreSQL亦需要安装相应的驱动 postgresql2

sqlite3示例

import 'dart:ffi';
import 'dart:io';
import 'package:moor_ffi/open_helper.dart';
import 'package:moor_ffi/database.dart';
import 'model.dart';

const _createTable = ''' 
create table if not exists paragraph (
  id integer primary key autoincrement,
  nick_name text,
  avatar text,
  content text,
  img_url text,
  like_no integer,
  tap_no integer,
  date text
);
''';

// 配置sqlite3的加载路径
DynamicLibrary _openOnWin() {
  final script = File(Platform.script.toFilePath()).parent;
  return DynamicLibrary.open('${script.path}/sqlite3.dll');
}

class DbTools {
  Database _db;

  DbTools(){
    open.overrideFor(OperatingSystem.windows, _openOnWin);
    _db = Database.open('db/data.db');
    _db.execute(_createTable);
  }
}

PostgreSql 示例

关于PostgreSql数据库的安装参见下一节

class PostgresDb{
  static const uri = 'postgres://postgres:123456@localhost:5432/test_db';
  Connection _conn;
  final Completer _completer = Completer();

  PostgresDb(){
     _init();
  }

  void _init() async{
    _conn = await connect(uri);
    await _conn.execute(_createTable2);
    _completer.complete(true);
  }

  void saveAll(List<ItemModel> data) async{
    // 等待初始化完成
    await _completer.future;
    for (var e in data) {
      var value = [e.nickName,e.avatarUrl,e.content,e.imgUrl,e.like,e.tap,e.date];
      await _conn.execute('insert into paragraph (nick_name,avatar,content,img_url,like_no,tap_no,date)'
       'values (@0,@1,@2,@3,@4,@5,@6)',value);
    }
  }

  void close()async{
    await _completer.future;
    _conn.close();
  }
}

公众号“编程之路从0到1”

20190301102949549

Copyright © Arcticfox 2020 all right reserved,powered by Gitbook文档修订于: 2024-06-09 20:22:55

results matching ""

    No results matching ""