PHP采集知识


序言

采集文章著名的:火车头、收费、自定义比较差

需要自己编写程序采集目标网站的信息为我所用。

采集必要的知识点:

  • 知道PHP发起网络请求的相关函数:
file_get_contents
fsockoptn
curl
  • 正则表达式/xpath
  • 了解一些html
  • http相关知识

使用curl进行采集

函数示例:

/**
 * @param string $url
 * @param array $data
 * @param array $header
 * @return bool|string
 */
function http_request(string $url, $data = [], array $header = [])
{
    $ret = '';
    // 初始化
    $ch = curl_init();
    // 相关配置
    # 设置请求的URL地址
    curl_setopt($ch, CURLOPT_URL, $url);
    # 设置一下执行成功后不直接返回到客户端
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    # 设置超时时间  单位是秒
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    # 不进行证数的检测
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    # 伪造一个请求的浏览器型号
    curl_setopt($ch, CURLOPT_USERAGENT, 'msie');

    // 表示有请求体,是post请求
    if (!empty($data)) {
        # 指明是一个post请求
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        if (is_string($data)) {
            # 设置头信息,告诉接受者我们发送的数据类型
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        }
    }

    // 执行
    $ret = curl_exec($ch);
    # 请求的错误码, 为0表示请求正确,大于0则表示请求失败
    if (curl_errno($ch) > 0) {
        echo curl_error($ch);
        exit;
    }

    // 关闭请求资源
    curl_close($ch);

    return $ret;
}

使用该方法来获取一个网站的title信息

<?php
/**
 * Author: Virus
 * Date: 2020/5/3
 * Time: 15:24
 * 采集标题练习1
 * 推荐使用curl,需要手动开启
 */

include __DIR__.'/function.php';

$url = "https://news.ke.com/bj/baike/0033/";

$html = http_request($url);

// 正则表达式来匹配出 title
// 修饰符 i 不区分大小写
// 修饰符 U 禁止贪婪
// 修饰符 s 忽略换行
//$preg = '/<title>(.*)<\/title>/iUs';
// 使用一个正则中没有用到的# 来进行定界符,里面的标签就不需要转义
$preg = '#<title>(.*)</title>#iUs';

preg_match_all($preg, $html, $arr);

print_r($arr);

xpath路径查询器

<?php
/**
 * Author: Virus
 * Date: 2020/5/3
 * Time: 15:24
 * 采集标题练习1
 * 推荐使用curl,需要手动开启
 */

include __DIR__.'/function.php';

$url = "https://news.ke.com/bj/baike/0033/";

$html = http_request($url);
// 声明一个dom对象
$dom = new DOMDocument();
// 忽略html不严格的格式
libxml_use_internal_errors(1);
$dom->loadHTML($html);
// 转为xpath对象
$xpath = new DOMXPath($dom);

/*
// 查询路径 xpath地址 chrome F12之后右键会有复制项:copy xpath
$query = '/html/head/title';

$nodeList = $xpath->query($query);

foreach ($nodeList as $item) {
    echo $item->nodeValue;
}
*/

// 查询所有图片
//$query = '//img/@data-original';
$query = '/html/body/div[3]/div[2]/div/div[2]/div[2]//img/@data-original';

$nodeList = $xpath->query($query);

foreach ($nodeList as $item) {
    echo $item->nodeValue."\n";
}

QueryList采集器使用

下载:

composer require jaeger/querylist:V3.2.1

使用它来实现上面的获取图片、标题、摘要和文章地址:

<?php

include __DIR__.'/function.php';
// 这个根据个人的地址进行加载
require '../vendor/autoload.php';

$url = "https://news.ke.com/bj/baike/0033/";

$html = http_request($url);

use QL\QueryList;

$dataList = QueryList::Query(
    $html,
    [
        // 获取图片
        'img' => ['.lj-lazy', 'data-original'],
        // 获取标题
        'title' => ['.item .text .LOGCLICK', 'text'],
        // 获取摘要
        'desc'  => ['.item .text .summary', 'text'],
        // 获取文章地址
        'href' => ['.item .text > a', 'href']
    ]
)->data;

var_dump($dataList);

使用QueryList采集分页数据后进行入库操作

这里的表字段desc当初因为没有使用倒引号 来进行包裹,而导致报错,因为在数据库中desc是关键字

<?php
/**
 * Author: Virus
 * Date: 2020/5/3
 * Time: 21:58
 */

// 永不超时 不能用nginx和apache,使用命令行
set_time_limit(0);
include __DIR__.'/function.php';
require '../vendor/autoload.php';

use QL\QueryList;

$db = new PDO('mysql:host=localhost;dbname=数据库名称;charset=utf8mb4', '用户名', '密码');

// 分页测试数据
$rang = range(1, 2);

foreach ($rang as $page) {
    $url  = 'https://news.ke.com/bj/baike/0033/pg'.$page.'/';
    $html = http_request($url);
    // 分析采集内容
    $dataList = QueryList::Query(
        $html,
        [
            // 获取图片
            'pic'   => ['.lj-lazy', 'data-original', '',  function ($item) {
                    // 得到扩展名
                    $ext = pathinfo($item, PATHINFO_EXTENSION);
                    // 生成文件名
                    $filename = md5($item).'_'.time().'.'.$ext;
                    // 生成的本地路径
                    $filepath = dirname(__DIR__).'/public/uploads/article/'.$filename;
                    file_put_contents($filepath, http_request($item));
                    return '/uploads/article/'.$filename;
            }],
            // 获取标题
            'title' => ['.item .text .LOGCLICK', 'text'],
            // 获取摘要
            'desc'  => ['.item .text .summary', 'text'],
            // 获取文章地址
            'url'  => ['.item .text > a', 'href'],
        ]
    )->data;

    // 入库
    foreach ($dataList as $val) {
        // 判断标题是否存在,存在则不进行入库

//        extract($val);
        // 添加的sql预处理 desc在sql中是关键字需要加上``
        $sql = "insert into articles (`title`,`desc`,`pic`,`url`,`body`) values (?,?,?,?,'')";
        $stmt = $db->prepare($sql);
        // 入库
        $stmt->execute([$val['title'], $val['desc'], $val['pic'], $val['url']]);
    }
}

入库的另外一种操作

// 入库
    foreach ($dataList as $val) {
        // 判断标题是否存在,存在则不进行入库

        extract($val); // 自动将数组转换成变量
        // 添加的sql预处理 desc在sql中是关键字需要加上``
        $sql = "insert into articles (`title`,`desc`,`pic`,`url`,`body`) values (?,?,?,?,'')";
        $stmt = $db->prepare($sql);
        // 入库
        $stmt->execute([$title, $desc, $pic, $url]);
    }

抓取内容页面

<?php
/**
 * Author: Virus
 * Date: 2020/5/3
 * Time: 21:58
 */

// 永不超时 不能用nginx和apache,使用命令行
set_time_limit(0);
include __DIR__.'/function.php';
require '../vendor/autoload.php';

use QL\QueryList;

$db = new PDO('mysql:host=localhost;dbname=数据库;charset=utf8mb4', '用户名', '密码');

// 内容页面
$sql = "select id,url from zfw_articles where body=''";
// row查询
$rows = $db->query($sql)->fetchAll(PDO::FETCH_ASSOC);

foreach ($rows as $item) {
    $id   = $item['id'];
    $url  = $item['url'];
    $html = http_request($url);
    // 分析采集内容
    $ret = QueryList::Query($html, ['body' => ['.bd', 'html']])->data;
    // 内容
    $body = $ret[0]['body'];
    // 入库

    // extract($val);
    // 添加的sql预处理 desc在sql中是关键字需要加上``
    $sql  = "update articles set body=? where id=?";
    $stmt = $db->prepare($sql);
    // 入库
    $stmt->execute([$body, $id]);
}

实现计划任务进行抓取

  • 先将代码上传到服务器 linux
  • 然后php手动执行php文件
# crontab
分   时   日   月   星期  脚本

# 每天凌晨1点抓取文章信息 /dev/null 黑洞(所有错误信息进去不显示) 2:失败 1:成功
0    1    *   *     *   php /xxx/xxx/list.php > dev/null 2>&1
# 每天2点抓取文章内容 成功或失败都不要显示(简写)  &>/dev/null
0    2    *   *     *  php /xxx/xxx/body.php &>/dev/null

文章作者: Virus
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Virus !
 上一篇
mysql实现主从复制和读写分离 mysql实现主从复制和读写分离
MySQL实现主从复制 在进行curd的同时,主服务器的复制数据库到从服务器中,保证数据不流失 环境介绍 系统环境:centos7.0 客户端连接工具:Xshell 远程文件传输工具:xftp 服务器(虚拟机): 主服务器:自
2020-05-05
下一篇 
DataTables的使用 DataTables的使用
分页 使用DataTables插件来实现分页,还带有很多的特殊效果 使用这个需要先引入jQuery的js文件和与之相关的css和js 分页分为: 客户端分页 优点:简单 缺点:数据多了,服务器加载慢、客户端加载慢、会崩溃 服务端分页
2020-05-01
  目录