爬蟲的故事

qixiaobo發表於2018-01-05

title: 爬蟲的故事 tags:

  • WebMagic
  • JFinal
  • Java
  • 爬蟲
  • spider categories: 爬蟲 date: 2017-07-25 18:18:53

1、爬蟲佔總PV比例較高,這樣浪費錢(尤其是三月份爬蟲)。

三月份爬蟲是個什麼概念呢?每年的三月份我們會迎接一次爬蟲高峰期。

最初我們百思不得其解。直到有一次,四月份的時候,我們刪除了一個url,然後有個爬蟲不斷的爬取url,導致大量報錯,測試開始找我們麻煩。我們只好特意為這個爬蟲釋出了一次站點,把刪除的url又恢復回去了。

但是當時我們的一個組員表示很不服,說,我們不能幹掉爬蟲,也就罷了,還要專門為它釋出,這實在是太沒面子了。於是出了個主意,說:url可以上,但是,絕對不給真實資料。

於是我們就把一個靜態檔案釋出上去了。報錯停止了,爬蟲沒有停止,也就是說對方並不知道東西都是假的。這個事情給了我們一個很大的啟示,也直接成了我們反爬蟲技術的核心:變更。

後來有個學生來申請實習。我們看了簡歷發現她爬過攜程。後來面試的時候確認了下,果然她就是四月份害我們釋出的那個傢伙。不過因為是個妹子,技術也不錯,後來就被我們招安了。現在已經快正式入職了。

後來我們一起討論的時候,她提到了,有大量的碩士在寫論文的時候會選擇爬取OTA資料,並進行輿情分析。因為五月份交論文,所以嘛,大家都是讀過書的,你們懂的,前期各種DotA,LOL,到了三月份了,來不及了,趕緊抓資料,四月份分析一下,五月份交論文。

就是這麼個節奏。

2、公司可免費查詢的資源被批量抓走,喪失競爭力,這樣少賺錢。

O他的價格可以在非登入狀態下直接被查詢,這個是底線。如果強制登陸,那麼可以通過封殺賬號的方式讓對方付出代價,這也是很多網站的做法。但是我們不能強制對方登入。那麼如果沒有反爬蟲,對方就可以批量複製我們的資訊,我們的競爭力就會大大減少。

競爭對手可以抓到我們的價格,時間長了使用者就會知道,只需要去競爭對手那裡就可以了,沒必要來攜程。這對我們是不利的。

3、爬蟲是否涉嫌違法? 如果是的話,是否可以起訴要求賠償?這樣可以賺錢。

這個問題我特意諮詢了法務,最後發現這在國內還是個擦邊球,就是有可能可以起訴成功,也可能完全無效。所以還是需要用技術手段來做最後的保障。

-----不願透露姓名的攜程酒店研發團隊崔廣宇

現在都大資料時代了,不會點爬蟲怎麼裝13,沒爬過github,知乎,豆瓣,twitter,fb怎麼做資料探勘 ,使用者畫像

-----某海龜網際網路精英

目前爬蟲和反爬蟲國內環境大家還是不願宣之於口的,網上一搜大部分都是一段python程式碼,然後大家懂得。

比較常見的幾個領域:電商(jd,tb),招聘(51job),新聞,部落格,社群(douban),旅遊(tuniu,攜程),服務類(58),房地產(鏈家)等

目前我們團隊主要使用Java語言,給大家介紹一個比較簡便的爬蟲框架 webmagic 趁著週末玩一把(請勿用於非法行為)

幾個注意事項

  1. 注意設定UA
  2. 注意使用代理(更換ip)
  3. 注意處理請求頻率
  4. 注意處理失敗請求
  5. 使用排程機制,方便掛掉後不需要重新開始
  6. 隨時關閉
  7. 監控爬蟲狀態
  8. 分散式爬取

簡單的對 XXX 畫面進行了爬取

目前使用了XX-NET做代理伺服器工程師必備技能之科學上網

通過Jfinal來完成db的持久化

    public class GitHubSpider implements PageProcessor {
        private Site site = Site.me().setRetryTimes(3).setSleepTime(1000).setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI");
        private static final String brandUrl = "http://XXX/level/brand/getAllJsons";
        private static final String manuUrl = "http://XXX/level/manufacturers/getManufacturers?brandId=#BRANDID";
        private static final String modelUrl = "http://XXX/level/model/getModels?brandId=#BRANDID&manufacturersId=#MANUID";
        private static final String saleUrl = "http://XXX/level/salesName/getSalesNames?brandId=#BRANDID&manufacturersId=#MANUID&modelId=#MODELID";
        private static final String yearUrl = "http://XXX/level/year/getYears?brandId=#BRANDID&manufacturersId=#MANUID&modelId=#MODELID&salesName=#SALEID";
        private static final String dataUrl = "http://XXX/level/fuchs/getData?compressId=#COMPRESSID";
        private static final String dataDetailAddressUrl = "http://XXX/level/fuchs/gotoDetailPage?productName=#PRODUECTNAME";
        private static final String dataDetailUrl = "http://XXX/#PRODUCTADDRESS";
     
        private static final JsonPathSelector brandIdSelector = new JsonPathSelector("$.value[*].brandId");
        private static final JsonPathSelector manuIdSelector = new JsonPathSelector("$.value[*].manufacturersId");
     
        private static final JsonPathSelector modelSelector = new JsonPathSelector("$.value[*].modelsId");
     
        private static final JsonPathSelector saleIdSelector = new JsonPathSelector("$.value[*].salesName");
        private static final JsonPathSelector yearIdSelector = new JsonPathSelector("$.value[*].compressId");
     
        @Override
        public void process(Page page) {
            String url = page.getUrl().get();
            if (url.contains("getAllJsons")) {
                page.addTargetRequests(Lists.transform(brandIdSelector.selectList(page.getRawText()), new Function<String, String>() {
                    @Override
                    public String apply(String brandId) {
                        return manuUrl.replace("#BRANDID", encodeParam(brandId));
                    }
                }));
                page.setSkip(true);
            } else if (url.contains("getManufacturers")) {
                final String brandId = page.getUrl().regex(".*brandId=(.*)", 1).get();
                page.addTargetRequests(Lists.transform(manuIdSelector.selectList(page.getRawText()), new Function<String, String>() {
                    @Override
                    public String apply(String manuId) {
                        return modelUrl.replace("#MANUID", encodeParam(manuId)).replace("#BRANDID", brandId);
                    }
                }));
                page.setSkip(true);
     
            } else if (url.contains("getModels")) {
                final String brandId = page.getUrl().regex(".*brandId=(.*)&manufacturersId.*", 1).get();
                final String manuId = page.getUrl().regex(".*manufacturersId=(.*)", 1).get();
                page.addTargetRequests(Lists.transform(modelSelector.selectList(page.getRawText()), new Function<String, String>() {
                    @Override
                    public String apply(String modelId) {
                        return saleUrl.replace("#MODELID", encodeParam(modelId)).replace("#MANUID", manuId).replace("#BRANDID", brandId);
                    }
                }));
                page.setSkip(true);
     
            } else if (url.contains("getSalesNames")) {
                final String brandId = page.getUrl().regex(".*brandId=(.*)&manufacturersId.*", 1).get();
                final String manuId = page.getUrl().regex(".*manufacturersId=(.*)&modelId.*", 1).get();
                final String modelId = page.getUrl().regex(".*modelId=(.*)", 1).get();
                page.addTargetRequests(Lists.transform(saleIdSelector.selectList(page.getRawText()), new Function<String, String>() {
                    @Override
                    public String apply(String saleName) {
                        return yearUrl.replace("#SALEID", encodeParam(saleName)).replace("#MODELID", modelId).replace("#MANUID", manuId).replace("#BRANDID", brandId);
                    }
                }));
                page.setSkip(true);
     
     
            } else if (url.contains("getYears")) {
     
                page.addTargetRequests(Lists.transform(yearIdSelector.selectList(page.getRawText()), new Function<String, String>() {
                    @Override
                    public String apply(String compressId) {
                        return dataUrl.replace("#COMPRESSID", encodeParam(compressId));
                    }
                }));
                page.setSkip(true);
     
            } else if (url.contains("getData")) {
                List<Selectable> nodes = page.getHtml().$("#VehicleInfoModel_modelShowArea table").nodes();
                Map<String, List<Oil>> map = Maps.newHashMapWithExpectedSize(nodes.size() / 2);
                StringBuilder stringBuilder = null;
                for (int i = 0; i < nodes.size(); i++) {
                    if (i % 2 == 0) {
                        stringBuilder = new StringBuilder();
                        Joiner.on(";").appendTo(stringBuilder, nodes.get(i).$("td", "text").all());
                    } else {
                        Selectable node = nodes.get(i);
                        Selectable titleNode = node.$("td[rowSpan]");
                        Selectable oilsNode = node.$("td[lang]");
                        List<String> rowSpanList = titleNode.$("td", "rowSpan").all();
                        List<String> titleNameList = titleNode.$("td", "text").all();
                        List<String> oilNameList = oilsNode.$("span", "text").all();
                        List<String> oilCapList = oilsNode.$("td", "text").all();
                        List<Oil> oilList = Lists.newArrayListWithExpectedSize(oilNameList.size());
                        int current = 0;
                        for (int j = 0; j < titleNameList.size(); j++) {
                            String title = titleNameList.get(j);
                            int row = Integer.parseInt(rowSpanList.get(j));
                            for (int k = 0; k < row; k++) {
                                Oil oil = new Oil();
                                oil.setCat(title);
                                oil.setCap(oilCapList.get(current));
                                oil.setName(oilNameList.get(current));
                                oil.set("cap", oil.getCap()).set("car", stringBuilder.toString()).set("cat", oil.getCat()).set("name", oil.getName());
                                oilList.add(oil);
                                current++;
                            }
     
                        }
                        map.put(stringBuilder.toString(), oilList);
                       /* List<Selectable> productNames = node.$("span", "text").nodes();
     
                        page.addTargetRequests(Lists.transform(productNames, new Function<Selectable, String>() {
                            @Override
                            public String apply(Selectable selectable) {
                                return dataDetailAddressUrl.replace("#PRODUECTNAME", encodeParam(selectable.get()));
                            }
                        }));*/
                    }
                }
                page.putField("oils", map);
            } else if (url.contains("gotoDetailPage")) {
                page.putField("value", dataDetailUrl.replace("#PRODUCTADDRESS", encodeParam(page.getRawText())));
            }
        }
     
        private String encodeParam(String param) {
            try {
                return URLEncoder.encode(URLEncoder.encode(param, "UTF-8"), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                return null;
            }
        }
     
        public Site getSite() {
            return site;
        }
     
        public static void main(String[] args) {
     
            HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
            httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(new Proxy("127.0.0.1", 8087)));
            DruidPlugin druidPlugin = new DruidPlugin("jdbc:mysql://192.168.1.7:3306/oil", "root", "root");
            ActiveRecordPlugin activeRecordPlugin = new ActiveRecordPlugin(druidPlugin);
            activeRecordPlugin.addMapping("oil", Oil.class);
            activeRecordPlugin.setDialect(new MysqlDialect());
            activeRecordPlugin.setContainerFactory(new CaseInsensitiveContainerFactory());
            druidPlugin.start();
            activeRecordPlugin.start();
            Spider.create(new GitHubSpider())
                    .addUrl(brandUrl)
                    .setDownloader(httpClientDownloader)
                    .addPipeline(new MysqlPipeline())
                    .setScheduler(new RedisScheduler("127.0.0.1"))
                    //開啟5個執行緒抓取
                    .thread(10)
                    //啟動爬蟲
                    .run();
        }
複製程式碼
    public class MysqlPipeline implements Pipeline {
        @Override
        public void process(ResultItems resultItems, Task task) {
            Map<String, List<Oil>> oils = resultItems.get("oils");
            if (oils != null && !oils.isEmpty()) {
                for (Map.Entry<String, List<Oil>> entry : oils.entrySet()) {
                    Db.batchSave(entry.getValue(),entry.getValue().size());
                }
            }
        }
    }
複製程式碼
    public class Oil extends Model<Oil> {
        private String name;
        private String cap;
        private String cat;
        public static final Oil dao = new Oil().dao();
        public String getName() {
            return name;
        }
     
        public void setName(String name) {
            this.name = name;
        }
     
        public String getCap() {
            return cap;
        }
     
        public void setCap(String cap) {
            this.cap = cap;
        }
     
        public String getCat() {
            return cat;
        }
     
        public void setCat(String cat) {
            this.cat = cat;
        }
     
     
        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Oil{");
            sb.append("name='").append(name).append('\'');
            sb.append(", cap='").append(cap).append('\'');
            sb.append(", cat='").append(cat).append('\'');
            sb.append('}');
            return sb.toString();
        }
    }
複製程式碼

輕鬆加愉快~當然python也很好用~~~

PS: 我是好孩子……

相關文章