爬蟲實現:根據IP地址反查域名

知行旅人發表於2019-04-13

域名解析與IP地址

域名解析是把域名指向網站空間IP,讓人們通過註冊的域名可以方便地訪問到網站的一種服務;IP地址是網路上標識站點的數字地址,為了方便記憶,採用域名來代替IP地址標識站點地址。域名解析就是域名到IP地址的轉換過程,該過程由DNS伺服器完成(來自百度百科)

先來了解兩個知識點

1、一個域名同一時刻只能對應一個IP地址

2、一個IP地址可以解析繫結多個域名,沒有限制

基於以上知識點,假如我們已知一個IP地址,我們怎麼才能獲取解析到該IP地址的所有域名資訊呢?一種方式是國家工信部能開放查詢介面以供查詢(不知道會不會開放?);另外一種方式就是接下來我要分享的——爬蟲實現:根據IP地址反查域名。

實現原理

實現原理其實很簡單,現在已有網站提供了根據IP地址查詢域名的功能,但是需要人為登入網站輸入IP地址查詢,我想要實現程式自動化查詢,所以就想到了爬蟲的方式,簡單來說,就是模擬人的查詢行為,將查詢結果解析成我想要的域名列表。

site.ip138.com為例,開啟F12,輸入一個IP查詢,觀察控制檯請求,看到下圖中資訊

請求地址為:site.ip138.com/119.75.217.…

請求方式為:GET

爬蟲實現:根據IP地址反查域名

然後,分析Response,可以看到,在頁面上看到的繫結域名資訊就是下圖紅框中的內容,所以只要能將Response的內容解析出來,獲取到的內容就可以得到想要的域名列表。

爬蟲實現:根據IP地址反查域名

上述Response是HTML頁面,使用jsoup來解析HTML簡直完美。

jsoup是什麼?

jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文字內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於jQuery的操作方法來取出和運算元據。

//解析成Document物件
Document document = Jsoup.parse(result);
if (document == null) {
    logger.error("Jsoup parse get document null!");
}
//根據ID屬性“list”獲取元素Element物件(有沒有感覺很像jQuery?)
Element listEle = document.getElementById("list");

//根據class屬性和屬性值篩選元素Element集合,並通過eachText()遍歷元素內容
return listEle.getElementsByAttributeValue("target", "_blank").eachText();
複製程式碼

result的內容通過HttpClient模擬HTTP請求

HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
httpGet.setHeader("Accept-Encoding", "gzip, deflate");
httpGet.setHeader("Accept-Language", "zh-CN,zh;q=0.9");
httpGet.setHeader("Cache-Control", "max-age=0");
httpGet.setHeader("Connection", "keep-alive");
httpGet.setHeader("Cookie", "Hm_lvt_d39191a0b09bb1eb023933edaa468cd5=1553090128; BAIDU_SSP_lcr=https://www.baidu.com/link?url=FS0ccst469D77DpdXpcGyJhf7OSTLTyk6VcMEHxT_9_&wd=&eqid=fa0e26f70002e7dd000000065c924649; pgv_pvi=6200530944; pgv_si=s4712839168; Hm_lpvt_d39191a0b09bb1eb023933edaa468cd5=1553093270");
httpGet.setHeader("DNT", "1");
httpGet.setHeader("Host", host);
httpGet.setHeader("Upgrade-Insecure-Requests", "1");
httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");

String result = HttpUtils.doGet(httpGet);
複製程式碼

HTTP請求工具類

public class HttpUtils {

    private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    public static String doGet(HttpGet httpGet) {
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClients.createDefault();
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(5000).setConnectionRequestTimeout(10000)
                    .setSocketTimeout(5000).build();
            httpGet.setConfig(requestConfig);
            HttpResponse httpResponse = httpClient.execute(httpGet);
            if (httpResponse.getStatusLine().getStatusCode() == 200 ||
                    httpResponse.getStatusLine().getStatusCode() == 302) {
                HttpEntity entity = httpResponse.getEntity();
                return EntityUtils.toString(entity, "utf-8");
            } else {
                logger.error("Request StatusCode={}", httpResponse.getStatusLine().getStatusCode());
            }
        } catch (Exception e) {
            logger.error("Request Exception={}:", e);
        } finally {
            if (httpClient != null) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    logger.error("關閉httpClient失敗", e);
                }
            }
        }
        return null;
    }
}
複製程式碼

新增Controller

@RestController
public class DomainSpiderController {

    private static Logger logger = LoggerFactory.getLogger(DomainSpiderController.class);

    @Autowired
    private DomainSpiderService domainSpiderService;

    /**
     * @param ip 119.75.217.109
     * @return
     */
    @RequestMapping("/spider/{ip}")
    @ResponseBody
    public List<String> domainSpider(@PathVariable("ip") String ip) {
        long startTime = System.currentTimeMillis();
        List<String> domains = domainSpiderService.domainSpiderOfIp138(ip);
        if(domains == null || domains.size() == 0) {
            domains = domainSpiderService.domainSpiderOfAizan(ip);
        }
        long endTime = System.currentTimeMillis();

        logger.info("完成爬蟲任務總耗時:{}s", (endTime - startTime) / 1000);

        return domains;
    }
}
複製程式碼

啟動Spring Boot應用,訪問瀏覽器:http://localhost:8080/spider/119.75.217.109 獲得返回結果如下:

爬蟲實現:根據IP地址反查域名

怎麼樣?是不是很簡單?

優化改進:有時候僅僅通過一個網站查詢的域名資料可能不太準確,甚至查詢不到資料,我們也沒法判斷誰才是正確的,所以,可以通過爬取多個網站的結果結合起來使用,例如:dns.aizhan.com

提出疑問:這些提供根據IP反查域名的網站,是怎麼實現的呢?我諮詢過其他人,他們的回答是這些網站收集了很多IP和域名的對應關係,真實情況是這樣的嗎?

示例原始碼

  • domain-spider

程式碼已上傳至碼雲Github上,歡迎下載學習

相關文章