当前位置 博文首页 > 高远的博客:CSDN爬虫(五)——CSDN用户(所有)爬取+常用爬虫正

    高远的博客:CSDN爬虫(五)——CSDN用户(所有)爬取+常用爬虫正

    作者:[db:作者] 时间:2021-09-06 22:43

    CSDN爬虫(五)——CSDN用户(所有)爬取+常用爬虫正则整理

    说明

    • 开发环境:jdk1.7+myeclipse10.7+win74bit+mysql5.5+webmagic0.5.2+jsoup1.7.2
    • 爬虫框架:webMagic
    • 建议:建议首先阅读webMagic的文档,再查看此系列文章,便于理解,快速学习:http://webmagic.io/
    • 开发所需jar下载(不包括数据库操作相关jar包):点我下载
    • 该系列文章会省略webMagic文档已经讲解过的相关知识。

    概述

    • 我们会从CSDN个人中心出发,首先爬取一个用户的个人信息。然后根据该用户的好友关系去爬取好友信息。依次类推,爬取所用用户。
    • 爬取CSDN所有用户是根据“粉丝、关注”去爬取“粉丝、关注”,必然会涉及到“死循环”。到后期肯定会出现大量“脏数据”(重复数据),就要考虑到过滤脏数据的问题。
    • 虽然用webMagic框架爬虫会用到大量的正则表达式,并且爬虫类的正则在网上也很少能找到资料,但是也是比较固定。

    CSDN用户(所有)爬取代码预览

    package com.wgyscsf.spider;
    
    import java.util.List;
    
    import org.jsoup.select.Elements;
    
    import us.codecraft.webmagic.Page;
    import us.codecraft.webmagic.Site;
    import us.codecraft.webmagic.Spider;
    import us.codecraft.webmagic.processor.PageProcessor;
    import us.codecraft.webmagic.selector.Html;
    
    import com.wgyscsf.utils.MyStringUtils;
    
    /**
     * @author 高远</n>
     * 编写日期   2016-9-24下午7:25:36</n>
     * 邮箱  wgyscsf@163.com</n>
     * 博客  http://blog.csdn.net/wgyscsf</n>
     * TODO</n>
     */
    public class CsdnMineSpider implements PageProcessor {
        private final String TAG = CsdnMineSpider.class.getSimpleName();
        private Site site = Site
                .me()
                .setDomain("my.csdn.net")
                .setSleepTime(1000)
                // 便于测试,休眠较长时间。
                .setUserAgent(
                        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
    
        @Override
        public void process(Page page) {
            // 列表页: 这里进行匹配,匹配出列表页进行相关处理。
            if ((page.getUrl()).regex("http://my.csdn.net/\\w+").match()) {
                // 获取最外层节点
                // TODO:应该是get(1),不知道为什么
                Elements mainElements = page.getHtml().getDocument()
                        .getElementsByTag("div").get(2).children();
                // 个人资料
                Elements profileElements = mainElements.get(0).getElementsByTag(
                        "div");
                // 个人技能
                Elements skillElements = mainElements.get(1)
                        .getElementsByTag("div");
                // 关系模块:关注和被关注
                Elements relationElements = mainElements.get(2).getElementsByTag(
                        "div");
    
                // 获取用户id
                String id_mine = MyStringUtils.getLastSlantContent(skillElements
                        .get(0).getElementsByTag("a").get(0).attr("href"));
                // 开始获取个人资料
                String headImg = profileElements.get(0)
                        .getElementsByClass("person-photo").get(0)
                        .getElementsByTag("img").attr("src");
                String fansNums = profileElements.get(0)
                        .getElementsByClass("fans_num").get(0)
                        .getElementsByTag("b").get(0).text();
                String nickname = profileElements.get(0)
                        .getElementsByClass("person-nick-name").get(0)
                        .getElementsByTag("span").get(0).text();
                // 这里只能精确到个人资料,没法继续分,因为好多用户该栏目只填写部分内容
                String personDetail = profileElements.get(0)
                        .getElementsByClass("person-detail").get(0).text();
    
                // 开始组织个人资料,保存数据操作
                System.out.println(TAG + ":用户id:" + id_mine + ",昵称:" + nickname
                        + ",粉丝:" + fansNums + ",个人资料概述:"
                        + personDetail + ",其它信息....");
    
                // 当前爬取页面信息爬取结束,将当前页面设置为“跳过”,下次再加入爬虫队列,直接过滤,提高爬取效率。
                page.setSkip(true);
                // 测试,看是否再被爬取。
                page.addTargetRequest("http://my.csdn.net/wgyscsf");
                /*
                 * 核心部分,从关系模块出发,去遍历所有相关用户!
                 */
                // 开始获取关注与被关注以及访客的个人中心。同时加入爬虫队列。
                String html = relationElements.get(0).html();
                List<String> all = new Html(html)
                        .xpath("//div[@class=\"mod_relations\"]").links().all();
                // 加入到爬虫队列
                page.addTargetRequests(all);
    
            }
        }
    
        @Override
        public Site getSite() {
            return site;
        }
    
        public static void main(String[] args) {
            Spider.create(new CsdnMineSpider())
                    .addUrl("http://my.csdn.net/wgyscsf").thread(1)// 便于测试,只开一个线程,正常爬起来,可以开15不成问题。
                    .pipeline(null)
                    .run();
        }
    
    }
    

    关键代码解释

    • 正则 http://my.csdn.net/\\w+"表示过滤出以http://my.csdn.net/"开头的网址链接。不过该正则存在一定的问题,就是只对网址前面部分进行了限制,并没有对后面进行限制。只要以http://my.csdn.net/"开头的网址全部会被加入到爬虫队列,存在大量的“脏数据”网址。比较合理的是:http://my.csdn.net/"后面可以出现除了“/”的任意字符。正则的准确性直接影响到爬虫的效率,越准确越好。这里不再修改。
    • profileElements.get(0).getElementsByClass("person-detail").get(0).text();该代码片段是为了获取用户的个人资料,该资料比较多,包括行业、职业、地区(国、省、市、区等)、姓名等信息。经过分析没有好的依据对信息进行归类,这里只是获取粗略信息。经过分析,甚至说CSDN官方最开始对该块没有进行合理的安排,导致用户的信息没有统一的格式。
    • 以下代码片段是该部分的核心代码,实现了递归式的爬取所有用户。relationElements元素来自于mainElements.get(2).getElementsByTag("div");,属于“关系模块”,包括关注的人和被关注的人以及访客信息。在这个模块中可以获取部分其它用户的id,只不过是部分的,最多只有6个人信息。CSDN官方没有提供获取所有粉丝或者关注者的信息。不过,只要这仅仅的用户信息,我们就可以爬取所有的用户。方便的是,顺便可以过滤掉“死鱼”用户(没有相互关系的用户)。

          // 开始获取关注与被关注以及访客的个人中心。同时加入爬虫队列。
          String html = relationElements.get(0).html();
          List<String> all = new Html(html)
                  .xpath("//div[@class=\"mod_relations\"]").links().all();
          // 加入到爬虫队列
          page.addTargetRequests(all);
      
    • 需要说明的是,爬取所有用户,其实不是一定要定位到“关系”模块。我们甚至可以简单粗暴的直接获取http://my.csdn.net/user_id"中所用有效链接,直接加入到爬虫队列。到时候直接通过正则http://my.csdn.net/\\w+"过滤出有效链接即可。代码如下:

          // 直接取出该网页下的所有网址链接,简单粗暴,不用进行判断。
          // 只要在进入爬取用户信息的时候加正则匹配即可。
          // 但是会出现过多的“脏数据”,增加判断,影响爬取效率。
          List<String> all = page.getHtml().links().all();
          System.out.println(all);// 测试打印的网址链接
          // 加入到爬虫队列
          page.addTargetRequests(all);
      
    • 正如前文概述中所说,递归时的爬取用户信息,会出现大量已经爬取过的页面,我们需要“过滤”掉这些信息。核心的代码是page.setSkip(true);,爬取之后,直接设置为“跳过”,下次爬取会直接跳过该链接。webMagic的作者对该方法的解释如下:

      /**

      • Set whether to skip the result.
      • Result which is skipped will not be processed by Pipeline.
        *
      • @param skip whether to skip the result
      • @return this
        */
    • 过滤代码如下:

          // 当前爬取页面信息爬取结束,将当前页面设置为“跳过”,下次再加入爬虫队列,直接过滤,提高爬取效率。
          page.setSkip(true);
          // 测试,看是否再被爬取。
          page.addTargetRequest("http://my.csdn.net/wgyscsf");
      

    爬取结果预览

    • 爬取所有用户
      这里写图片描述
    • 爬取所有http://my.csdn.net/user_id"内网址
      这里写图片描述

    常用爬虫正则

    • http://my.csdn.net/\\w+" :过滤出所有以http://my.csdn.net/开头的网址。
    • ^http://blog.csdn.net/((?!/).)*$:过滤出以http://blog.csdn.net/开头,并且后面不能再出现“/”的所有网址链接。
    • ^http://blog.csdn.net/\\w+/article/list/[0-9]*[1-9][0-9]*$:过滤出http://blog.csdn.net/后面是任意字符,并且紧接着/article/list/,且/article/list/后面只能是数字的所有网址链接。
    • http://blog.csdn.net/\\w+/article/details/\\w+:效果同上,只是最后允许任意字符,不仅仅限于数字。

    测试正则的方式

        Pattern pattern = Pattern
                .compile("^http://blog.csdn.net/\\w+/article/list/[0-9]*[1-9][0-9]*$");
        Matcher matcher = pattern
                .matcher("http://blog.csdn.net/wgyscsf/article/list/32423");
        boolean b = matcher.matches();
        // 当条件满足时,将返回true,否则返回false
        System.out.println(b);
    

    操作代码(代码已全部迁移至github,欢迎star)

    https://github.com/scsfwgy/WebMagic_CSDN_Demo

    个人公众号,及时更新技术文章

    cs