教你写个QQ机器人(4)让你的机器人学会看妹子
市场调研
炎炎夏日,唯有空调和西瓜(西瓜皮不知道是什么垃圾的请看上一节【JAVA】教你写个QQ机器人(3)让你的机器人学会垃圾分类),才能抵御住热浪的侵袭与蝉鸣的噪耳。此时此刻,如果再有一个姣好的妹子,那就十分快哉了。当然,妹子是不可能有的,但我们可以实现他。
功能需求
发送“看妹子,来n(n是具体数字,0<n<=30)张”,则机器人回复n张妹子图。听起来是不是很有意思,来让我们开始动手吧。
资源获取
花瓣网可是个好东西啊,里面的图的质量都非常的高。网站链接花瓣_美女模块
我们利用Jsoup直接抓取img标签就可以了,但实践下来发现,图片是js动态加载出来的,而且还有下拉加载。
先看一下妹子图片的url构成,例如
https://hbimg.huabanimg.com/22e0be71929e945750b27f617e07ae73fc51e5482ce6b8-J5YenR_fw658
多看几个图片的地址,我们就会发现,图片的地址由https://hbimg.huabanimg.com/加key构成,这个key怎么获取,我们继续探索。
首先利用Jsoup获取网页源码的body部分
Element element = Jsoup.connect(URLConst.HUABAN_MZ_INDEX).get().body();
System.out.println(element);
body部分是一个json数组,我们主要关心pins数组中的对象,包涵pin_id(下拉刷新需要用到),file.key(就是上述的key),file.type(图片类型)
app.page["board"] = {
.....
"pins": [
{
"pin_id": 2577135993,
....
"file": {
...
"key": "ebdf851738f3641ac99bad620e29216fcf4fa466afbc5-Rt8jP4",
"type": "image/jpeg",
...
},
...
},
{
"pin_id": 2577134964,
....
"file": {
...
"key": "4b460d009b23dff2e59c0ca8965473245a98452d1f4837-OTHmRy",
"type": "image/jpeg",
...
},
...
}...
现在我们需要做的,就是利用正则表达式,将属性设置进对象中
@Test
public void getMZ() throws IOException {
Element element = Jsoup.connect(URLConst.HUABAN_MZ_INDEX).get().body();
List<MzImg> mzImgList=parsePinsFromXml(element.toString());
mzImgList.forEach(System.out::println);
}
private List<MzImg> parsePinsFromXml(String xmlStr) {
List<MzImg> pins = new ArrayList<MzImg>();
String pattern = "\\{\"pin_id\":(\\d+),.+?\"key\":\"(.+?)\",.\"type\":\"image/(.+?)\",";
// 创建 Pattern 对象
Pattern r = Pattern.compile(pattern);
// 现在创建 matcher 对象
Matcher m = r.matcher(xmlStr);
while (m.find()) {
MzImg pin = new MzImg();
pin.setPinId(m.group(1));
pin.setKey(m.group(2));
pin.setType(m.group(3));
pins.add(pin);
}
return pins;
}
其中最后四个
MzImg{pinId='2518975235', key='adc818007febafb3031f0019d72426982bae22372d0e7-Aasf9n', type='jpeg'}
MzImg{pinId='2518975147', key='fedb28ec155d69be514d8a374519312e4573f4322a743-MeT2Gg', type='jpeg'}
MzImg{pinId='2518974230', key='8e211f60e459518a4165b3dfdc204f2822d121c8153dbe-hcCdUL', type='jpeg'}
MzImg{pinId='2518974067', key='24b6c089d69a8e547dbdd894c30826bbb20cb8324a483a-3iQC10', type='png'}
到这一步,我们已经能获取第1页的图片url,接下来我们处理下拉刷新
废话不多说,直接F12,观察下拉的url
Request URL: https://huaban.com/boards/481662/?jy55g2cp&max=2518974067&limit=20&wfl=1
结合遍历输出的最后四个的属性值,可以看得出max值为第一页中最后一个图片的pinId,limit为每页显示多少张图片,wfl为下拉前的页数。
至此,我们已经能抓取妹子图片了,下一步我们将之与QQ机器人结合起来。
大展身手的机器人
整体代码见我的GithubQQ机器人,目前拥有垃圾分类、看妹子(嘶~)、关键词监控功能
我们对机器人进行私聊,handleReceiveMsg依据消息种类转发给handlePrivateMsg方法
/**
* 由收到的消息类型转发给特定的方法
*
* @param receiveMsg
*/
private ReplyMsg handleReceiveMsg(ReceiveMsg receiveMsg) {
String post_type = receiveMsg.getPost_type();
switch (post_type) {
case "message":
//收到消息
String message_type = receiveMsg.getMessage_type();
switch (message_type) {
case "private":
//私聊消息
return privateMsgService.handlePrivateMsg(receiveMsg);
...
}
break;
...
}
return null;
}
其中handlePrivateMsg方法又调用了sendMZPicByMsg方法
public ReplyMsg handlePrivateMsg(ReceiveMsg receiveMsg) {
Long user_id = receiveMsg.getUser_id();
String raw_message = receiveMsg.getRaw_message();
//查询指令是否标准,不标准则反馈提示
if (MsgUtil.getMenu(raw_message) != null) {
ReplyMsg replyMsg = new ReplyMsg();
replyMsg.setReply(MsgUtil.getMenu(raw_message));
return replyMsg;
}
//kmz
if (raw_message.contains("看妹子")) {
kmzService.sendMZPicByMsg(receiveMsg);
}
...
return null;
}
KmzServiceImpl类的全部代码
package com.sun.kq.service.impl;
import com.sun.kq.constant.FileConst;
import com.sun.kq.constant.URLConst;
import com.sun.kq.enums.MessageType;
import com.sun.kq.model.*;
import com.sun.kq.service.GroupMsgService;
import com.sun.kq.service.KmzService;
import com.sun.kq.service.PrivateMsgService;
import com.sun.kq.util.ImageDownloadUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
public class KmzServiceImpl implements KmzService {
@Autowired
KmzService kmzService;
@Autowired
GroupMsgService groupMsgService;
@Autowired
PrivateMsgService privateMsgService;
@Override
public ReplyMsg sendMZPicByMsg(ReceiveMsg receiveMsg) {
String raw_message = receiveMsg.getRaw_message();
int n = 1;
try {
n = Integer.parseInt(raw_message.split("来")[1].split("张")[0]);
} catch (Exception e) {
}
if (n < 0 || n > 30) {
ReplyMsg replyMsg = new ReplyMsg();
replyMsg.setReply("求求你做个正常的人");
replyMsg.setAt_sender(true);
return replyMsg;
}
List<String> urlList = kmzService.getKmzImageKey(n);
for (String key : urlList) {
try {
boolean exist = ImageDownloadUtil.isImgExist(key);
if (!exist) {
ImageDownloadUtil.downloadImg(key);
System.out.println("图像不存在");
} else {
System.out.println("图像存在");
}
} catch (Exception e) {
e.printStackTrace();
}
String message = "[CQ:image,file=" + FileConst.KMZ_IMG_PREFIX + key + ".jpg]";
switch (MessageType.getEnumByType(receiveMsg.getMessage_type())) {
case GROUP:
GroupMsg groupMsg = new GroupMsg();
groupMsg.setMessage(message);
groupMsg.setGroup_id(receiveMsg.getGroup_id());
groupMsgService.sendGroupMsg(groupMsg);
break;
case PRIVATE:
PrivateMsg privateMsg = new PrivateMsg();
privateMsg.setMessage(message);
privateMsg.setUser_id(receiveMsg.getUser_id());
privateMsgService.sendPrivateMsg(privateMsg);
break;
}
}
return null;
}
@Override
public List<String> getKmzImageKey(int n) {
List<String> keyList = new ArrayList<>();
try {
Element element = Jsoup.connect(URLConst.HUABAN_MZ_INDEX).get().body();
List<MzImg> mzImgList = parsePinsFromXml(element.toString());
String lastPid = mzImgList.get(mzImgList.size() - 1).getPinId();
for (int page = 1; page <= n / 2 + 1; page++) {
Element tempElement = Jsoup.connect("https://huaban.com/boards/481662/?jxt53umu&max=" + lastPid + "&limit=20&wfl=" + page).get().body();
List<MzImg> tempUrlList = parsePinsFromXml(tempElement.toString());
lastPid = tempUrlList.get(tempUrlList.size() - 1).getPinId();
mzImgList.addAll(tempUrlList);
}
while (true) {
int random = new Random().nextInt(mzImgList.size() - 1);
String key = mzImgList.get(random).getKey();
if (!keyList.contains(key)) {
keyList.add(key);
}
if (keyList.size() == n) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return keyList;
}
private List<MzImg> parsePinsFromXml(String xmlStr) {
List<MzImg> pins = new ArrayList<MzImg>();
String pattern = "\\{\"pin_id\":(\\d+),.+?\"key\":\"(.+?)\",.\"type\":\"image/(.+?)\",";
// 创建 Pattern 对象
Pattern r = Pattern.compile(pattern);
// 现在创建 matcher 对象
Matcher m = r.matcher(xmlStr);
while (m.find()) {
MzImg pin = new MzImg();
pin.setPinId(m.group(1));
pin.setKey(m.group(2));
pin.setType(m.group(3));
pins.add(pin);
System.out.println(pin.getPinId() + "," + pin.getKey() + "," + pin.getType());
}
return pins;
}
}
当用户发送“看妹子,来n张”时,我们就在1~n/2+1页数中随机抽取n张不同的图片, 并下载到酷Q安装目录下的data/image中,之后利用CQ码,CQ码中携带data/image中图片的地址,再将CQ码封装在PrivateMsg中,最后对发起用户执行n次的sendPrivateMsg操作。
到这里,所有的代码就编写完成了。
一睹芳容
ok,大工告成。
作为一个屌丝程序员,每天在空闲的时间做一些有意思的项目,实属是最大的期待了。