序言
采集文章著名的:火车头、收费、自定义比较差
需要自己编写程序采集目标网站的信息为我所用。
采集必要的知识点:
- 知道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