java spring+mybatis 整合实现爬虫之《今日头条》搞笑动态图片爬取
ccwgpt 2024-10-13 01:34 32 浏览 0 评论
先上效果图
抓取的动态图:
数据库:
一.此爬虫介绍
今日头条本身就是做爬虫的,爬取各大网站的图片文字信息,再自己整合后推送给用户,特别是里面的动态图片,很有意思。在网上搜了搜,大多都是用Python来写的,本人是学习javaweb这块的,对正则表达式也不是很熟悉,就想着能不能换个我熟悉的方式来写。此爬虫使用spring+mybatis框架整合实现,使用mysql数据库保存爬取的数据,用jsoup来操作HTML的标签节点(完美避开正则表达式),获取文章中动态图片的链接,通过响应头中“Content-Type”的值来判断图片的格式,再将图片保存在本地。当然也可以爬取里面的文字,比如一些搞笑的黄段子,在此基础上稍加改动就可以实现,此爬虫只是提供一个入门的思路,更多好玩的爬虫玩法还待大家去开发,哈哈。
二.技术选型
核心语言:java; 核心框架:spring; 持久层框架:mybatis; 数据库连接池:Alibaba Drui; 日志管理:Log4j; jar包管理:maven; 。。。。
三.找规律,划重点
打开头条首页,找到点击搞笑模块,点击F12,下载后加载下一页,发现是通过ajax请求api来获取的数据,如下图:
这里写图片描述
这是响应的json数据,里面的参数和值顾名思义大家都懂得。
这里写图片描述
是ajax访问就好解决了,通过我百度谷歌各种研究后发现,ajax请求的前三个参数是不变的,改变category参数是请求不同的模块,本列子是请求的搞笑模块所以值为funny,max_behot_time和max_behot_time_tmp这两个参数值是时间戳,首次请求是0,之后的值是响应json数据里面的next中的值。as和cp值是通过一段js生成的,其实就是一个加密了的时间戳而已。js代码后面会贴。
四.开始搭框架撸代码
项目搭建后之后为下图所示的文件结构,不懂得自行谷歌 哈哈
不多说直接上核心代码了:
package io.z77z.main;
import io.z77z.dao.FunnyMapper;
import io.z77z.entity.Funny;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.UUID;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
public class TouTiaoCrawler {
// 搞笑板块的api地址
public static final String FUNNY = "http://www.toutiao.com/api/pc/feed/?utm_source=toutiao&widen=1";
// 头条首页地址
public static final String TOUTIAO = "http://www.toutiao.com";
// 使用"spring.xml"和"spring-mybatis.xml"这两个配置文件创建Spring上下文
static ApplicationContext ac = new ClassPathXmlApplicationContext(
"spring-mybatis.xml");
// 从Spring容器中根据bean的id取出我们要使用的funnyMapper对象
static FunnyMapper funnyMapper = (FunnyMapper) ac.getBean("funnyMapper");
// 接口访问次数
private static int refreshCount = 0;
// 时间戳
private static long time = 0;
public static void main(String[] args) {
System.out.println("----------开始干活!-----------------");
while (true) {
crawler(time);
}
}
public static void crawler(long hottime) {// 传入时间戳,会获取这个时间戳的内容
refreshCount++;
System.out.println("----------第" + refreshCount + "次刷新------返回的请求时间为:"
+ hottime + "----------");
String url = FUNNY + "&max_behot_time=" + hottime
+ "&max_behot_time_tmp=" + hottime;
JSONObject param = getUrlParam(); // 获取用js代码得到的as和cp的值
// 定义接口访问的模块
/*
* __all__ : 推荐 news_hot: 热点 funny:搞笑
*/
String module = "funny";
url += "&as=" + param.get("as") + "&cp=" + param.get("cp")
+ "&category=" + module;
JSONObject json = null;
try {
json = getReturnJson(url);// 获取json串
} catch (Exception e) {
e.printStackTrace();
}
if (json != null) {
time = json.getJSONObject("next").getLongValue("max_behot_time");
JSONArray data = json.getJSONArray("data");
for (int i = 0; i < data.size(); i++) {
try {
JSONObject obj = (JSONObject) data.get(i);
// 判断这条文章是否已经爬过
if (funnyMapper.selectByGroupId((String) obj
.get("group_id")) != null) {
System.out
.println("----------此文章已经爬过啦!-----------------");
continue;
}
// 访问页面返回document对象
String url1 = TOUTIAO + "/a" + obj.getString("group_id");
Document document = getArticleInfo(url1);
System.out.println("----------成功访问了文章:" + url1
+ "-----------------");
// 将document也存入
obj.put("document", document.toString());
// 将json对象转换成java Entity对象
Funny funny = JSON.parseObject(obj.toString(), Funny.class);
// json入库
funny.setBehotTime(new Date());
funnyMapper.insertSelective(funny);
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
System.out.println("----------返回的json列表为空----------");
}
}
// 访问接口,返回json封装的数据格式
public static JSONObject getReturnJson(String url) {
try {
URL httpUrl = new URL(url);
BufferedReader in = new BufferedReader(new InputStreamReader(
httpUrl.openStream(), "UTF-8"));
String line = null;
String content = "";
while ((line = in.readLine()) != null) {
content += line;
}
in.close();
return JSONObject.parseObject(content);
} catch (Exception e) {
System.err.println("访问失败:" + url);
e.printStackTrace();
}
return null;
}
// 获取网站的document对象
public static Document getArticleInfo(String url) {
try {
Connection connect = Jsoup.connect(url);
Document document;
document = connect.get();
Elements article = document.getElementsByClass("article-content");
if (article.size() > 0) {
Elements a = article.get(0).getElementsByTag("img");
if (a.size() > 0) {
for (Element e : a) {
String url2 = e.attr("src");
// 下载img标签里面的图片到本地
saveToFile(url2);
}
}
}
return document;
} catch (IOException e) {
System.err.println("访问文章页失败:" + url + " 原因" + e.getMessage());
return null;
}
}
// 执行js获取as和cp参数值
public static JSONObject getUrlParam() {
JSONObject jsonObject = null;
FileReader reader = null;
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
String jsFileName = "toutiao.js"; // 读取js文件
reader = new FileReader(jsFileName); // 执行指定脚本
engine.eval(reader);
if (engine instanceof Invocable) {
Invocable invoke = (Invocable) engine;
Object obj = invoke.invokeFunction("getParam");
jsonObject = JSONObject.parseObject(obj != null ? obj
.toString() : null);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return jsonObject;
}
// 通过url获取图片并保存在本地
public static void saveToFile(String destUrl) {
FileOutputStream fos = null;
BufferedInputStream bis = null;
HttpURLConnection httpUrl = null;
URL url = null;
String uuid = UUID.randomUUID().toString();
String fileAddress = "d:\\imag/" + uuid;// 存储本地文件地址
int BUFFER_SIZE = 1024;
byte[] buf = new byte[BUFFER_SIZE];
int size = 0;
try {
url = new URL(destUrl);
httpUrl = (HttpURLConnection) url.openConnection();
httpUrl.connect();
String Type = httpUrl.getHeaderField("Content-Type");
if (Type.equals("image/gif")) {
fileAddress += ".gif";
} else if (Type.equals("image/png")) {
fileAddress += ".png";
} else if (Type.equals("image/jpeg")) {
fileAddress += ".jpg";
} else {
System.err.println("未知图片格式");
return;
}
bis = new BufferedInputStream(httpUrl.getInputStream());
fos = new FileOutputStream(fileAddress);
while ((size = bis.read(buf)) != -1) {
fos.write(buf, 0, size);
}
fos.flush();
System.out.println("图片保存成功!地址:" + fileAddress);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassCastException e) {
e.printStackTrace();
} finally {
try {
fos.close();
bis.close();
httpUrl.disconnect();
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}
}复制代码
获取as和cp参数的js代码
function getParam(){
var asas;
var cpcp;
var t = Math.floor((new Date).getTime() / 1e3)
, e = t.toString(16).toUpperCase()
, i = md5(t).toString().toUpperCase();
if (8 != e.length){
asas = "479BB4B7254C150";
cpcp = "7E0AC8874BB0985";
}else{
for (var n = i.slice(0, 5), o = i.slice(-5), a = "", s = 0; 5 > s; s++){
a += n[s] + e[s];
}
for (var r = "", c = 0; 5 > c; c++){
r += e[c + 3] + o[c];
}
asas = "A1" + a + e.slice(-3);
cpcp= e.slice(0, 3) + r + "E1";
}
return '{"as":"'+asas+'","cp":"'+cpcp+'"}';
}
!function(e) {
"use strict";
function t(e, t) {
var n = (65535 & e) + (65535 & t)
, r = (e >> 16) + (t >> 16) + (n >> 16);
return r << 16 | 65535 & n
}
function n(e, t) {
return e << t | e >>> 32 - t
}
function r(e, r, o, i, a, u) {
return t(n(t(t(r, e), t(i, u)), a), o)
}
function o(e, t, n, o, i, a, u) {
return r(t & n | ~t & o, e, t, i, a, u)
}
function i(e, t, n, o, i, a, u) {
return r(t & o | n & ~o, e, t, i, a, u)
}
function a(e, t, n, o, i, a, u) {
return r(t ^ n ^ o, e, t, i, a, u)
}
function u(e, t, n, o, i, a, u) {
return r(n ^ (t | ~o), e, t, i, a, u)
}
function s(e, n) {
e[n >> 5] |= 128 << n % 32,
e[(n + 64 >>> 9 << 4) + 14] = n;
var r, s, c, l, f, p = 1732584193, d = -271733879, h = -1732584194, m = 271733878;
for (r = 0; r < e.length; r += 16)
s = p,
c = d,
l = h,
f = m,
p = o(p, d, h, m, e[r], 7, -680876936),
m = o(m, p, d, h, e[r + 1], 12, -389564586),
h = o(h, m, p, d, e[r + 2], 17, 606105819),
d = o(d, h, m, p, e[r + 3], 22, -1044525330),
p = o(p, d, h, m, e[r + 4], 7, -176418897),
m = o(m, p, d, h, e[r + 5], 12, 1200080426),
h = o(h, m, p, d, e[r + 6], 17, -1473231341),
d = o(d, h, m, p, e[r + 7], 22, -45705983),
p = o(p, d, h, m, e[r + 8], 7, 1770035416),
m = o(m, p, d, h, e[r + 9], 12, -1958414417),
h = o(h, m, p, d, e[r + 10], 17, -42063),
d = o(d, h, m, p, e[r + 11], 22, -1990404162),
p = o(p, d, h, m, e[r + 12], 7, 1804603682),
m = o(m, p, d, h, e[r + 13], 12, -40341101),
h = o(h, m, p, d, e[r + 14], 17, -1502002290),
d = o(d, h, m, p, e[r + 15], 22, 1236535329),
p = i(p, d, h, m, e[r + 1], 5, -165796510),
m = i(m, p, d, h, e[r + 6], 9, -1069501632),
h = i(h, m, p, d, e[r + 11], 14, 643717713),
d = i(d, h, m, p, e[r], 20, -373897302),
p = i(p, d, h, m, e[r + 5], 5, -701558691),
m = i(m, p, d, h, e[r + 10], 9, 38016083),
h = i(h, m, p, d, e[r + 15], 14, -660478335),
d = i(d, h, m, p, e[r + 4], 20, -405537848),
p = i(p, d, h, m, e[r + 9], 5, 568446438),
m = i(m, p, d, h, e[r + 14], 9, -1019803690),
h = i(h, m, p, d, e[r + 3], 14, -187363961),
d = i(d, h, m, p, e[r + 8], 20, 1163531501),
p = i(p, d, h, m, e[r + 13], 5, -1444681467),
m = i(m, p, d, h, e[r + 2], 9, -51403784),
h = i(h, m, p, d, e[r + 7], 14, 1735328473),
d = i(d, h, m, p, e[r + 12], 20, -1926607734),
p = a(p, d, h, m, e[r + 5], 4, -378558),
m = a(m, p, d, h, e[r + 8], 11, -2022574463),
h = a(h, m, p, d, e[r + 11], 16, 1839030562),
d = a(d, h, m, p, e[r + 14], 23, -35309556),
p = a(p, d, h, m, e[r + 1], 4, -1530992060),
m = a(m, p, d, h, e[r + 4], 11, 1272893353),
h = a(h, m, p, d, e[r + 7], 16, -155497632),
d = a(d, h, m, p, e[r + 10], 23, -1094730640),
p = a(p, d, h, m, e[r + 13], 4, 681279174),
m = a(m, p, d, h, e[r], 11, -358537222),
h = a(h, m, p, d, e[r + 3], 16, -722521979),
d = a(d, h, m, p, e[r + 6], 23, 76029189),
p = a(p, d, h, m, e[r + 9], 4, -640364487),
m = a(m, p, d, h, e[r + 12], 11, -421815835),
h = a(h, m, p, d, e[r + 15], 16, 530742520),
d = a(d, h, m, p, e[r + 2], 23, -995338651),
p = u(p, d, h, m, e[r], 6, -198630844),
m = u(m, p, d, h, e[r + 7], 10, 1126891415),
h = u(h, m, p, d, e[r + 14], 15, -1416354905),
d = u(d, h, m, p, e[r + 5], 21, -57434055),
p = u(p, d, h, m, e[r + 12], 6, 1700485571),
m = u(m, p, d, h, e[r + 3], 10, -1894986606),
h = u(h, m, p, d, e[r + 10], 15, -1051523),
d = u(d, h, m, p, e[r + 1], 21, -2054922799),
p = u(p, d, h, m, e[r + 8], 6, 1873313359),
m = u(m, p, d, h, e[r + 15], 10, -30611744),
h = u(h, m, p, d, e[r + 6], 15, -1560198380),
d = u(d, h, m, p, e[r + 13], 21, 1309151649),
p = u(p, d, h, m, e[r + 4], 6, -145523070),
m = u(m, p, d, h, e[r + 11], 10, -1120210379),
h = u(h, m, p, d, e[r + 2], 15, 718787259),
d = u(d, h, m, p, e[r + 9], 21, -343485551),
p = t(p, s),
d = t(d, c),
h = t(h, l),
m = t(m, f);
return [p, d, h, m]
}
function c(e) {
var t, n = "";
for (t = 0; t < 32 * e.length; t += 8)
n += String.fromCharCode(e[t >> 5] >>> t % 32 & 255);
return n
}
function l(e) {
var t, n = [];
for (n[(e.length >> 2) - 1] = void 0,
t = 0; t < n.length; t += 1)
n[t] = 0;
for (t = 0; t < 8 * e.length; t += 8)
n[t >> 5] |= (255 & e.charCodeAt(t / 8)) << t % 32;
return n
}
function f(e) {
return c(s(l(e), 8 * e.length))
}
function p(e, t) {
var n, r, o = l(e), i = [], a = [];
for (i[15] = a[15] = void 0,
o.length > 16 && (o = s(o, 8 * e.length)),
n = 0; 16 > n; n += 1)
i[n] = 909522486 ^ o[n],
a[n] = 1549556828 ^ o[n];
return r = s(i.concat(l(t)), 512 + 8 * t.length),
c(s(a.concat(r), 640))
}
function d(e) {
var t, n, r = "0123456789abcdef", o = "";
for (n = 0; n < e.length; n += 1)
t = e.charCodeAt(n),
o += r.charAt(t >>> 4 & 15) + r.charAt(15 & t);
return o
}
function h(e) {
return unescape(encodeURIComponent(e))
}
function m(e) {
return f(h(e))
}
function g(e) {
return d(m(e))
}
function v(e, t) {
return p(h(e), h(t))
}
function y(e, t) {
return d(v(e, t))
}
function b(e, t, n) {
return t ? n ? v(t, e) : y(t, e) : n ? m(e) : g(e)
}
"function" == typeof define && define.amd ? define("static/js/lib/md5", ["require"], function() {
return b
}) : "object" == typeof module && module.exports ? module.exports = b : e.md5 = b
}(this)复制代码
五.最后
我还发现了头条有个简约版,研究后发现这个简约版应该更好玩一些。
这里写图片描述
访问的格式是p+页码,直接读取每页里面的链接,就可以进行爬取了,就不再通过json串来获取文章地址,也不需要传什么限制参数,在本项目上稍加改动就可以了
这里写图片描述
这里写图片描述
六.JUST DO IT
> 。。。。。。。。。。。。。。。。。。。。。。
原文链接:https://juejin.cn/post/6844903459360079879
相关推荐
- 腾讯开源框架TarsCpp-rpc设计分析-server(二)
-
2Tars协议2.1是什么借用官方说法:TARS编码协议是一种数据编解码规则,它将整形、枚举值、字符串、序列、字典、自定义结构体等数据类型按照一定的规则编码到二进制数据流中。对端接收到二进制数据流...
- 微服务调用为什么用RPC框架,http不更简单吗?
-
简单点,HTTP是协议,RPC是概念!实现RPC可以基于HTTP协议(Feign),TCP协议(Netty),RMI协议(Soap),WebService(XML—RPC)框架。传输过程中,也因为序列...
- go-zero:开箱即用的微服务框架(gin框架微服务)
-
go-zero是一个集成了各种工程实践的Web和rpc框架,它的弹性设计保障了大并发服务端的稳定性,并且已经经过了充分的实战检验。go-zero在设计时遵循了“工具大于约定和文档”的理...
- SOFARPC :高性能、高扩展性、生产级的 Java RPC 框架
-
#暑期创作大赛#SOFARPC是一个高性能、高扩展性、生产级的JavaRPC框架。在蚂蚁金服,SOFARPC已经使用了十多年,已经发展了五代。SOFARPC致力于简化应用程序之间的RPC...
- 自研分布式高性能RPC框架及服务注册中心ApiRegistry实践笔记
-
痛点1.bsf底层依赖springcloud,影响bsf更新springboot新版本和整体最新技术版本升级。2.eureka已经闭源,且框架设计较重,同时引入eureka会自行引入较多sprin...
- Rust语言从入门到精通系列 - Tonic RPC框架入门实战
-
Rust语言是一种系统级语言,被誉为“没有丧失性能的安全语言”。Rust语言的优势在于其内存安全机制,在编译时就能保证程序的内存安全。Tonic模块是Rust语言的一个RPC(RemoteProce...
- 腾讯开源框架TarsCpp-rpc设计分析-client(一)
-
前言Tars是腾讯开源的微服务平台,包含了一个高性能的rpc框架和服务治理平台,TarsCpp是其C++版本。对于以C++为主要开发语言,同时还想深入了解rpc和微服务框架具体实现的同学来说,Tars...
- 设计了一款TPS百万级别的分布式、高性能、可扩展的RPC框架
-
为啥要开发RPC框架事情是这样的,在开发这个RPC框架之前,我花费了不少时间算是对Dubbo框架彻底研究透彻了。冰河在撸透了Dubbo2.x和Dubbo3.x的源码之后,本来想给大家写一个Dubbo源...
- rpc框架使用教程,超级稳定好用,大厂都在使用
-
rpc是什么远程调用协议如何使用导入依赖<dependency><groupId>org.apache.dubbo</groupId><art...
- Layui 框架实战:动态加载 Select 与二级联动全解析
-
在现代Web开发中,下拉选择框(Select)是用户输入数据时不可或缺的组件。很多时候,我们需要的选项并非静态写死在HTML中,而是需要根据业务逻辑从后端动态获取。更有甚者,我们可能需要实现“...
- 15个能为你节省数百小时的前端设计神器,从UI库到文档生成
-
无论你是刚开始开发之旅的新手,还是疲于应付生产期限的资深程序员,有一个真理始终不变:正确的工具能彻底改变你的工作流程。多年来,我测试了数百个开发工具——有些实用,大多数平庸。但有一批免费网站经受住了时...
- Layui与WinForm通用权限管理系统全解析
-
嘿,小伙伴们,今天咱们来聊聊Layui和WinForm这两个框架在通用权限管理系统中的应用。别担心,我会尽量用简单易懂的语言来讲解,保证让大家都能跟上节奏!首先说说Layui。Layui是一个前端UI...
- 纯Python构建精美UI!MonsterUI让前端开发效率飙升
-
“无需CSS知识,告别类名记忆,11行代码实现专业级卡片组件”在传统Web开发中,构建美观界面需要同时掌握HTML、CSS、JavaScript三剑客,开发者不得不在多种语言间频繁切换。即使使用Boo...
- WebTUI:将终端用户界面(TUI)之美带到浏览器的CSS库
-
在当今Web技术飞速发展的时代,界面设计愈发复杂多样。然而,随着现代化工具的广泛使用,一些开发者开始回归极简风格,追求一种简洁而富有韵味的设计。WebTUI正是这样一款CSS库,它将经典的终...
- 人教版二年级下册生字描红汇总(拼音+笔顺+描红),可打印!
-
可定制内容,评论区留言。本次整理的为人教版二年级下册所有生字,共计300个;写字是小学阶段一项重要的基本功训练,把汉字写得正确、工整、美观,可以提高运用汉字这一交际工具的准确性和效率。对小学生进行写字...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 腾讯开源框架TarsCpp-rpc设计分析-server(二)
- 微服务调用为什么用RPC框架,http不更简单吗?
- go-zero:开箱即用的微服务框架(gin框架微服务)
- SOFARPC :高性能、高扩展性、生产级的 Java RPC 框架
- 自研分布式高性能RPC框架及服务注册中心ApiRegistry实践笔记
- Rust语言从入门到精通系列 - Tonic RPC框架入门实战
- 腾讯开源框架TarsCpp-rpc设计分析-client(一)
- 设计了一款TPS百万级别的分布式、高性能、可扩展的RPC框架
- rpc框架使用教程,超级稳定好用,大厂都在使用
- Layui 框架实战:动态加载 Select 与二级联动全解析
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- JAVA集合框架 (47)
- mfc框架 (52)
- abb框架断路器 (48)
- ui自动化框架 (47)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- tornado框架 (48)
- 前端框架bootstrap (54)
- ppt框架 (48)
- 内联框架 (52)
- cad怎么画框架 (58)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)