社工入门之如何分析一个人的作息规律
电影中的社工高手可以很轻易操控目标对象,使其愿意去做某项事(科幻片拍的像玄幻片…),虽然现实没有那么玄幻,但是真实的社工,或者你想要了解某个人,必然会有一个环节就是通过社交网络之类的对目标对象进行建模,即用一些特征来描述目标对象(也可想象为对人贴标签)。举一个例子我们平时如果加了一个新的QQ好友,可能会去QQ空间、腾讯微博等看下他以往的历史记录来确认他是个怎样的人,因为人的思维通常比较简单,所以当接触到一个陌生的人时急切需要在他身上寻找符合某个标签的某个特征,一旦这家伙显现出来某个特征,好,赶紧把对应的标签给他贴上,这样贴几个标签可以确定以后与其相处的策略,虽然可能比较偏激,但大多数人基本上都是这么干的。虽然你们可能没有说过几句话,但经过这么一番调查之后你已经对这个人有了一些比较深入的了解,至少是自以为有深入了解 :)
所以社工这玩意儿基本人人都会,说白了就是信息收集,如果是经常在网络上活动的人,留下的痕迹比较多,甚至有可能画出一条时间轴,能够大致描述他的人生轨迹,这个可以对自己感兴趣的人试一试,个人推荐使用时间轴作为主线的方式来分析。
对于信息收集,其中一个比较小的点就是如何分析一个人的作息规律,从而推测出特定的时间点他很有可能在做什么,比如统计某个人习惯在什么时间点刷知乎、泡论坛之类的,这个我有一个思路就是对于某类信息比较大,比如论坛上的留言,知乎上的点赞之类,可以通过爬虫技术先采集再分析。我想对自己有个更深刻的了解,所以这里就用一个例子,写一个小小的爬虫来分析我发博客的时间分布情况。
首先确定要抓取的字段,其实只需要发布时间字段就可以了,但是想到为了以后可能还会有其它的分析,就把其它方便抓取的字段比如阅读量、评论数之类一并抓取了。
PostMetaInfo:
package cc11001100.crawler; import java.util.Date; /** * @author CC11001100 */ public class PostMetaInfo { private String id; private String title; private Date postTime; // like uv but not private Integer visited; private Integer reviewNum; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Date getPostTime() { return postTime; } public void setPostTime(Date postTime) { this.postTime = postTime; } public Integer getVisited() { return visited; } public void setVisited(Integer visited) { this.visited = visited; } public Integer getReviewNum() { return reviewNum; } public void setReviewNum(Integer reviewNum) { this.reviewNum = reviewNum; } }
CnBlogPostMetaInfoCrawler:
package cc11001100.crawler; import com.alibaba.fastjson.JSON; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.File; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static java.util.stream.Collectors.toList; /** * 博客园活跃时间统计 * * @author CC11001100 */ public class CnBlogPostMetaInfoCrawler { private static final Logger log = LogManager.getLogger(CnBlogPostMetaInfoCrawler.class); private static void grabMetaInfoByUserName(String username) { final String savePath = getSavePathByUserName(username); final String urlPattern = String.format("http://www.cnblogs.com/%s/default.html?page=", username); for (int pageNum = 1; true; pageNum++) { String currentPageListUrl = urlPattern + pageNum; List<PostMetaInfo> parseResultList = parsePostList(currentPageListUrl); if (parseResultList.isEmpty()) { break; } save(parseResultList, savePath); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { log.info("InterruptedException"); } } } private static String getSavePathByUserName(String username) { return "CnBlog_" + username + "_post_meta_info.data"; } private static void save(List<PostMetaInfo> postMetaInfoList, String savePath) { List<String> lineList = postMetaInfoList.stream().map(JSON::toJSONString).collect(toList()); try { FileUtils.writeLines(new File(savePath), "UTF-8", lineList, "\n", true); } catch (IOException e) { log.info("save list to file={} failed", savePath); } } private static List<PostMetaInfo> parsePostList(String listUrl) { String htmlResponse = getHtml(listUrl); if (StringUtils.isBlank(htmlResponse)) { log.error("url={}, cannot get html content", listUrl); return Collections.emptyList(); } Document document = Jsoup.parse(htmlResponse); return document.select(".postDesc").stream().map(elt -> { PostMetaInfo postMetaInfo = new PostMetaInfo(); String text = elt.ownText(); String id = extractByPattern(elt.select(">a").attr("href"), "postid=(\\d+)"); postMetaInfo.setId(id); String title = document.select(String.format("a.postTitle2[href~=/p/%s.html]", postMetaInfo.getId())).text(); postMetaInfo.setTitle(title); Date postTime = extractByPatternAndToDate(text, "posted @ (\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2})", "yyyy-MM-dd HH:mm"); postMetaInfo.setPostTime(postTime); postMetaInfo.setVisited(extractByPatternAndToInt(text, "阅读\\((\\d+)\\)")); postMetaInfo.setVisited(extractByPatternAndToInt(text, "评论\\((\\d+)\\)")); return postMetaInfo; }).collect(toList()); } private static String extractByPattern(String content, String pattern) { Matcher matcher = Pattern.compile(pattern).matcher(content); if (matcher.find()) { return matcher.group(1); } return ""; } private static int extractByPatternAndToInt(String content, String extractPattern) { String rawString = extractByPattern(content, extractPattern); try { return Integer.parseInt(rawString); } catch (NumberFormatException e) { log.info("NumberFormatException, string={}", rawString); } return 0; } private static Date extractByPatternAndToDate(String content, String extractPattern, String dateFormatPattern) { String rawString = extractByPattern(content, extractPattern); try { return new SimpleDateFormat(dateFormatPattern).parse(rawString); } catch (ParseException e) { log.info("SimpleDateFormat parse exception, string={}", rawString); } return null; } private static String getHtml(String url) { final int DEFAULT_RETRY_TIMES = 10; for (int i = 1; i <= DEFAULT_RETRY_TIMES; i++) { try { return Jsoup.connect(url).execute().body(); } catch (IOException e) { log.info("url={}, retry={}", url, i); } } return ""; } private static void show(final String username) { final String savePath = getSavePathByUserName(username); try { List<String> postMetaInfoList = FileUtils.readLines(new File(savePath), "UTF-8"); postMetaInfoList.stream().map(postMetaInfo -> JSON.parseObject(postMetaInfo, PostMetaInfo.class).getPostTime().getHours()) .collect(Collectors.groupingBy(x -> x)) .forEach((k, v) -> { // 柱状图总共使用1000个小条条,根据屏幕大小进行调节选择最舒适的视图 int length = (int) (v.size() * 1000.0 / postMetaInfoList.size()); System.out.printf("%2s : %s %d\n", k, StringUtils.repeat("=", length), v.size()); }); } catch (IOException e) { log.info("read file error, path={}", savePath); } } public static void main(String[] args) { final String username = "CC11001100"; grabMetaInfoByUserName(username); show(username); } }
上面的代码做的事情就是爬取某个人的所有博客,并将发布时间以小时为单位做一个group by … count,然后使用横向的柱状图画一个简单的图表,我的图表如下:
时间大部分集中在23:00~02:00之间,从上面的图表可以看出这个家伙很经常熬夜,由此恶毒的猜测他应该需要搞点枸杞泡着喝 :(
.