كيف يمكنني "مسح" موقع ويب (أو صفحة) للحصول على معلومات وإدخالها في برنامجي؟

StackOverflow https://stackoverflow.com/questions/2835505

  •  26-09-2019
  •  | 
  •  

سؤال

حسنًا، أحاول إلى حد كبير معرفة كيفية سحب المعلومات من صفحة ويب وإدخالها في برنامجي (في Java).

على سبيل المثال، إذا كنت أعرف الصفحة المحددة التي أريد الحصول على معلومات منها، ومن أجل التبسيط، صفحة عنصر Best Buy، فكيف يمكنني الحصول على المعلومات المناسبة التي أحتاجها من تلك الصفحة؟مثل العنوان والسعر والوصف؟

ماذا يمكن أن تسمى هذه العملية؟ليس لدي أي فكرة حتى عن البدء في البحث في هذا الأمر.

يحرر:حسنًا، أقوم بإجراء اختبار لـ JSoup (الاختبار الذي نشرته BalusC)، ولكني أتلقى هذا الخطأ باستمرار:

Exception in thread "main" java.lang.NoSuchMethodError: java.util.LinkedList.peekFirst()Ljava/lang/Object;
at org.jsoup.parser.TokenQueue.consumeWord(TokenQueue.java:209)
at org.jsoup.parser.Parser.parseStartTag(Parser.java:117)
at org.jsoup.parser.Parser.parse(Parser.java:76)
at org.jsoup.parser.Parser.parse(Parser.java:51)
at org.jsoup.Jsoup.parse(Jsoup.java:28)
at org.jsoup.Jsoup.parse(Jsoup.java:56)
at test.main(test.java:12)

لدي أباتشي كومنز

هل كانت مفيدة؟

المحلول

استخدم محلل HTML مثل Jsoup.هذا له تفضيلي فوق موزعي HTML الأخرى المتوفرة في جافا منذ ذلك الحين يدعم مسج يحب محددات CSS.أيضًا، تمثل فئتها قائمة العقد، Elements, ، ينفذ Iterable بحيث يمكنك التكرار عليه في معززة للحلقة (لذلك ليس هناك حاجة إلى المتاعب مع مطول Node و NodeList مثل الفئات في محلل Java DOM المتوسط).

إليك مثالًا أساسيًا لبدء التشغيل (فقط ضع أحدث ملف Jsoup JAR في مسار الفصل):

package com.stackoverflow.q2835505;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Test {

    public static void main(String[] args) throws Exception {
        String url = "https://stackoverflow.com/questions/2835505";
        Document document = Jsoup.connect(url).get();

        String question = document.select("#question .post-text").text();
        System.out.println("Question: " + question);

        Elements answerers = document.select("#answers .user-details a");
        for (Element answerer : answerers) {
            System.out.println("Answerer: " + answerer.text());
        }
    }

}

كما كنت قد خمنت، فإن هذا يطبع سؤالك الخاص وأسماء جميع المجيبين.

نصائح أخرى

يشار إلى هذا على أنه تجريف على الشاشة ، ويكيبيديا لديها هذه المقالة على أكثر تحديدا تجريف على شبكة الإنترنت. يمكن أن يكون هذا تحديًا كبيرًا لأن هناك بعض القبيح ، والفوضى ، والمكسورة-غير المتقلب ، HTML هناك ، حظًا سعيدًا جدًا.

سأستخدم jtidy - إنه Simlar to Jsoup ، لكنني لا أعرف Jsoup جيدًا. JTidy يعالج HTML المكسور ويعيد مستند W3C ، بحيث يمكنك استخدام هذا كمصدر لـ XSLT لاستخراج المحتوى الذي تهتم به حقًا. إذا كنت لا تعرف XSLT ، فيمكنك أيضًا الذهاب مع JSUP ، مثل المستند النموذج أجمل للعمل مع W3C.

تحرير: إلقاء نظرة سريعة على موقع JSoup يوضح أن JSoup قد يكون بالفعل الخيار الأفضل. يبدو أنه يدعم محددات CSS خارج المربع لاستخراج الأشياء من المستند. قد يكون هذا أسهل كثيرًا في العمل من خلال الدخول إلى XSLT.

يمكنك استخدام محلل HTML (العديد من الروابط المفيدة هنا: Java HTML Parser).

تسمى العملية "Grabbing Website Content". ابحث في "Grab Website Content Java" لمزيد من الانحراف.

JSoup يدعم Java 1.5

https://github.com/tburch/jsoup/commit/d8ea84f46e009a7f144e414a9fa73ea187019a3

يبدو أن هذا المكدس كان خطأً ، وقد تم إصلاحه

حل JSoup رائع ، ولكن إذا كنت بحاجة إلى استخراج شيء بسيط حقًا ، فقد يكون من الأسهل استخدام regex أو string.indexof

كما ذكر الآخرون بالفعل ، فإن العملية تسمى الكشط

ربما ترغب في إلقاء نظرة على HTML لمعرفة ما إذا كان يمكنك العثور على سلاسل فريدة من نوعها وبالقرب من النص الخاص بك ، ثم يمكنك استخدام خطوط الخطوط/char-Offs للوصول إلى البيانات.

يمكن أن تكون محرجة في جافا ، إذا لم تكن هناك فئات XML مماثلة لتلك الموجودة في System.XML.Linq في C#.

يمكنك أيضا المحاولة جارفست.

يعتمد على DSL JRUBY على محرك جافا النقي إلى مواقع الويب الخاصة بـ spider-scrape.

مثال:

ابحث عن جميع الروابط داخل صفحة ويب (wget و xpath هي بنيات لغة Jarvest):

wget | xpath('//a/@href')

داخل برنامج Java:

Jarvest jarvest = new Jarvest();
  String[] results = jarvest.exec(
    "wget | xpath('//a/@href')", //robot! 
    "http://www.google.com" //inputs
  );
  for (String s : results){
    System.out.println(s);
  }

ربما لن تكون إجابتي مفيدة لكاتب هذا السؤال (لقد تأخرت لمدة 8 أشهر، لذا ليس التوقيت المناسب على ما أعتقد) ولكن أعتقد أنها ستكون مفيدة على الأرجح للعديد من المطورين الآخرين الذين قد يصادفون هذه الإجابة.

اليوم، قمت للتو بإصدار (باسم شركتي) إطار عمل HTML إلى POJO الكامل الذي يمكنك استخدامه لتعيين HTML إلى أي فئة POJO مع بعض التعليقات التوضيحية ببساطة.المكتبة نفسها سهلة الاستخدام للغاية وتتميز بالعديد من الأشياء الأخرى في حين أنها قابلة للتوصيل بشكل كبير.يمكنك إلقاء نظرة عليها هنا: https://github.com/whimtrip/jwht-htmltopojo

كيف تستعمل :الأساسيات

تخيل أننا بحاجة إلى تحليل صفحة html التالية:

<html>
    <head>
        <title>A Simple HTML Document</title>
    </head>
    <body>
        <div class="restaurant">
            <h1>A la bonne Franquette</h1>
            <p>French cuisine restaurant for gourmet of fellow french people</p>
            <div class="location">
                <p>in <span>London</span></p>
            </div>
            <p>Restaurant n*18,190. Ranked 113 out of 1,550 restaurants</p>  
            <div class="meals">
                <div class="meal">
                    <p>Veal Cutlet</p>
                    <p rating-color="green">4.5/5 stars</p>
                    <p>Chef Mr. Frenchie</p>
                </div>

                <div class="meal">
                    <p>Ratatouille</p>
                    <p rating-color="orange">3.6/5 stars</p>
                    <p>Chef Mr. Frenchie and Mme. French-Cuisine</p>
                </div>

            </div> 
        </div>    
    </body>
</html>

لنقم بإنشاء POJOs التي نريد تعيينها لها:

public class Restaurant {

    @Selector( value = "div.restaurant > h1")
    private String name;

    @Selector( value = "div.restaurant > p:nth-child(2)")
    private String description;

    @Selector( value = "div.restaurant > div:nth-child(3) > p > span")    
    private String location;    

    @Selector( 
        value = "div.restaurant > p:nth-child(4)"
        format = "^Restaurant n\*([0-9,]+). Ranked ([0-9,]+) out of ([0-9,]+) restaurants$",
        indexForRegexPattern = 1,
        useDeserializer = true,
        deserializer = ReplacerDeserializer.class,
        preConvert = true,
        postConvert = false
    )
    // so that the number becomes a valid number as they are shown in this format : 18,190
    @ReplaceWith(value = ",", with = "")
    private Long id;

    @Selector( 
        value = "div.restaurant > p:nth-child(4)"
        format = "^Restaurant n\*([0-9,]+). Ranked ([0-9,]+) out of ([0-9,]+) restaurants$",
        // This time, we want the second regex group and not the first one anymore
        indexForRegexPattern = 2,
        useDeserializer = true,
        deserializer = ReplacerDeserializer.class,
        preConvert = true,
        postConvert = false
    )
    // so that the number becomes a valid number as they are shown in this format : 18,190
    @ReplaceWith(value = ",", with = "")
    private Integer rank;

    @Selector(value = ".meal")    
    private List<Meal> meals;

    // getters and setters

}

والآن Meal الطبقة كذلك :

public class Meal {

    @Selector(value = "p:nth-child(1)")
    private String name;

    @Selector(
        value = "p:nth-child(2)",
        format = "^([0-9.]+)\/5 stars$",
        indexForRegexPattern = 1
    )
    private Float stars;

    @Selector(
        value = "p:nth-child(2)",
        // rating-color custom attribute can be used as well
        attr = "rating-color"
    )
    private String ratingColor;

    @Selector(
        value = "p:nth-child(3)"
    )
    private String chefs;

    // getters and setters.
}

لقد قدمنا ​​المزيد من التوضيحات حول الكود أعلاه على صفحة جيثب الخاصة بنا.

في الوقت الحالي، دعونا نرى كيفية إلغاء هذا.

private static final String MY_HTML_FILE = "my-html-file.html";

public static void main(String[] args) {


    HtmlToPojoEngine htmlToPojoEngine = HtmlToPojoEngine.create();

    HtmlAdapter<Restaurant> adapter = htmlToPojoEngine.adapter(Restaurant.class);

    // If they were several restaurants in the same page, 
    // you would need to create a parent POJO containing
    // a list of Restaurants as shown with the meals here
    Restaurant restaurant = adapter.fromHtml(getHtmlBody());

    // That's it, do some magic now!

}


private static String getHtmlBody() throws IOException {
    byte[] encoded = Files.readAllBytes(Paths.get(MY_HTML_FILE));
    return new String(encoded, Charset.forName("UTF-8"));

}

يمكن العثور على مثال قصير آخر هنا

نأمل أن يساعد هذا شخص ما هناك!

انظر إلى مكتبة حليقة. لم أستخدمها أبدًا في Java ، لكنني متأكد من أنه يجب أن يكون هناك روابط لذلك. في الأساس ، ما ستفعله هو إرسال طلب حليقة إلى أي صفحة تريد "كشط". سيقوم الطلب بإرجاع سلسلة مع رمز المصدر إلى الصفحة. من هناك ، سوف تستخدم Regex لتحليل أي البيانات التي تريدها من الكود المصدري. هذا عمومًا كيف ستفعل ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top