Ich habe Diffbot zum ersten Mal im Jahr 2021 entdeckt, als ich eine Demo ihrer APIs für den Adobe Developer-Blog erstellt habe ( „Natural Language Processing, Adobe PDF Extract, and Deep PDF Intelligence“ ).
Damals war ich beeindruckt, wie einfach die API von Diffbot war und wie schnell sie reagierte. Ich hatte mir ihre API eine Weile nicht angesehen, aber vor ein paar Tagen kündigten sie neue Unterstützung für das Zusammenfassen von Text an. Ich dachte, dies wäre eine großartige Möglichkeit, es mit der Adobe PDF Extract API zu kombinieren. Hier ist, was ich gefunden habe.
Wenn Sie dies selbst ausprobieren möchten, benötigen Sie zunächst Folgendes:
Okay, schauen wir uns an, wie ein Zusammenfassungsfluss funktionieren könnte.
Die Extract API (tut mir leid, die „Adobe PDF Extract API“, warte, das ist mein Blog, ich kann Dinge kürzen!) ist ziemlich leistungsfähig. Mithilfe von KI wird eine PDF-Datei intelligent analysiert, um jedes einzelne Elementdetail im Dokument korrekt zu finden.
Also Text, Schriftarten, Farben, Position usw. Es kann auch Bilder und Tabellendaten finden, was zu einigen ziemlich leistungsstarken Anwendungsfällen führt. (Ein gutes Beispiel hierfür finden Sie in meinem Blogbeitrag , in dem ich mehrere wissenschaftliche Zeitschriften durchsuche, um astronomische Daten zu sammeln, zu aggregieren und Berichte zu erstellen.)
Für diese Demo brauchen wir im wahrsten Sinne des Wortes nur den Text. Dafür verwende ich die REST-APIs. Der „Ablauf“ für fast alle Aspekte der verfügbaren PDF-Dienste ist:
Beachten Sie, dass Sie auch SDKs verwenden können, aber ich finde unsere REST-APIs so einfach, dass ich die Endpunkte einfach direkt anspreche. Hier ist das Skript, das ich geschrieben habe, um den Extraktionsprozess durchzuführen. Es ist im Grunde alles, was ich oben gesagt habe und auf eine Quell-PDF-Datei in meinem lokalen Dateisystem verweist.
/* This demo is a two step process. This file, step one, handles extracting and storing the JSON from a PDF. */ import 'dotenv/config'; import fs from 'fs'; import { Readable } from 'stream'; import { finished } from 'stream/promises'; const SOURCE_PDF = '../../source_pdfs/boring_adobe_security_doc.pdf'; const REST_API = "https://pdf-services.adobe.io/"; const CLIENT_ID = process.env.CLIENT_ID; const CLIENT_SECRET = process.env.CLIENT_SECRET; async function delay(x) { return new Promise(resolve => { setTimeout(() => resolve(), x); }); } async function getAccessToken(id, secret) { const params = new URLSearchParams(); params.append('client_id', id); params.append('client_secret', secret); let resp = await fetch('https://pdf-services-ue1.adobe.io/token', { method: 'POST', headers: { 'Content-Type':'application/x-www-form-urlencoded' }, body:params }); let data = await resp.json(); return data.access_token; } async function getUploadData(mediaType, token, clientId) { let body = { 'mediaType': mediaType }; body = JSON.stringify(body); let req = await fetch(REST_API+'assets', { method:'post', headers: { 'X-API-Key':clientId, 'Authorization':`Bearer ${token}`, 'Content-Type':'application/json' }, body: body }); let data = await req.json(); return data; } async function uploadFile(url, filePath, mediaType) { let stream = fs.createReadStream(filePath); let stats = fs.statSync(filePath); let fileSizeInBytes = stats.size; let upload = await fetch(url, { method:'PUT', redirect:'follow', headers: { 'Content-Type':mediaType, 'Content-Length':fileSizeInBytes }, duplex:'half', body:stream }); if(upload.status === 200) return; else { throw('Bad result, handle later.'); } } async function pollJob(url, token, clientId) { let status = null; let asset; while(status !== 'done') { let req = await fetch(url, { method:'GET', headers: { 'X-API-Key':clientId, 'Authorization':`Bearer ${token}`, } }); let res = await req.json(); status = res.status; if(status === 'done') { asset = res; } else { await delay(2000); } } return asset; } async function downloadFile(url, filePath) { let res = await fetch(url); const body = Readable.fromWeb(res.body); const download_write_stream = fs.createWriteStream(filePath); return await finished(body.pipe(download_write_stream)); } async function extractJob(asset, token, clientId) { let body = { 'assetID': asset.assetID } let resp = await fetch(REST_API + 'operation/extractpdf', { method: 'POST', headers: { 'Authorization':`Bearer ${token}`, 'X-API-KEY':clientId, 'Content-Type':'application/json' }, body:JSON.stringify(body) }); return resp.headers.get('location'); } let accessToken = await getAccessToken(CLIENT_ID, CLIENT_SECRET); console.log('Got our access token.'); let uploadedAsset = await getUploadData('application/pdf', accessToken, CLIENT_ID); await uploadFile(uploadedAsset.uploadUri, SOURCE_PDF, 'application/pdf'); console.log('Source PDF Uploaded.'); let job = await extractJob(uploadedAsset, accessToken, CLIENT_ID); console.log('Job created. Now to poll it.'); let result = await pollJob(job, accessToken, CLIENT_ID); console.log('Job is done.'); await downloadFile(result.content.downloadUri, 'extract.json'); console.log('All done.');
Ok, hoffentlich liest du noch. Im Allgemeinen versuche ich zu vermeiden, solche riesigen Codeblöcke zu veröffentlichen, aber wenn Sie sich auf die Zeilen am Ende konzentrieren, werden Sie sehen, dass ich nur auf Hilfsfunktionen stoße, die das tun, was ich im obigen Ablauf beschrieben habe. Authentifizieren Sie sich, bitten Sie um das Hochladen einer PDF-Datei, starten Sie einen Auftrag, überprüfen Sie ihn und laden Sie das Ergebnis herunter.
Eine Anmerkung möchte ich hinzufügen. Extract gibt eine ZIP-Datei zurück, die einen JSON-Ergebnissatz und optional Tabellen und Bilder enthält. Eine schöne Sache an der REST-API ist, dass ich direkt auf den JSON zugreifen und ihn einfach speichern kann.
Das JSON-Ergebnis kann ziemlich groß sein. Für mein Quell-PDF (ein unglaublich langweiliges Adobe-Sicherheitsdokument) mit drei Seiten ist das resultierende JSON 4560 Zeilen lang. Mein Quell-PDF finden Sie hier und die Rohausgabe von Extract hier .
Anstatt hier alle 4,5.000 Zeilen einzufügen, möchte ich einen Ausschnitt zeigen – zwei einzigartige Elemente, die von der API gefunden wurden:
{ "Bounds": [ 44.62139892578125, 756.9429931640625, 245.0037841796875, 766.3184967041016 ], "Font": { "alt_family_name": "* Arial", "embedded": true, "encoding": "Identity-H", "family_name": "* Arial", "font_type": "CIDFontType0", "italic": false, "monospaced": false, "name": "*Arial-6539", "subset": false, "weight": 400 }, "HasClip": false, "Lang": "en", "Page": 0, "Path": "//Document/Sect/P", "Text": "Adobe Vendor Security Review Program White Paper ", "TextSize": 8.5, "attributes": { "SpaceAfter": 18 } }, { "Bounds": [ 0.0, 0.0, 630.0, 820.7799987792969 ], "ClipBounds": [ 548.72802734375, 739.1929931640625, 602.5444488525391, 820.7799987792969 ], "Page": 0, "Path": "//Document/Sect/Figure", "attributes": { "BBox": [ 548.9779999999737, 739.4429999999993, 587.61599999998, 790.920999999973 ], "Placement": "Block" } },
Im obigen Beispiel sehen Sie, dass das erste Element ein Textelement ist und eine Text
Eigenschaft enthält, während das zweite eine Zahl ist. Für meine Demo muss ich nur die Text
Eigenschaft verwenden, sofern sie vorhanden ist. Sehen wir uns das in Aktion an.
Ich habe bereits erwähnt, dass die Diffbot-API recht einfach zu verwenden ist. Lassen Sie mich das demonstrieren.
Zuerst richte ich einige Variablen ein und lese den JSON-Code ein, den ich im ersten Schritt erhalten habe. Um es klarzustellen: Ich könnte alles in einem Prozess erledigen, aber es macht wirklich keinen Sinn, Extract mehr als einmal auszuführen. Das Coole daran ist, dass ich das Ergebnis tatsächlich mehrfach aufrufen könnte.
Eine weitere coole Funktion von Diffbot besteht beispielsweise darin, Entitäten aus Text abzurufen, also aus dem, worüber ein Dokument spricht (Personen, Orte usw.). Wie auch immer, hier ist der Anfang:
/* In this file, we take the result from our Extract operation and pass it to Diffbot */ import 'dotenv/config'; import fs from 'fs'; const DIFFBOT_KEY = process.env.DIFFBOT_KEY; const SOURCE_JSON = './extract.json'; const data = JSON.parse(fs.readFileSync(SOURCE_JSON, 'utf8')); console.log(`Read in source data from ${SOURCE_JSON}.`);
Als nächstes muss ich den Text aus dem Extraktionsergebnis analysieren:
let text = data.elements.reduce((text, el) => { if(el.Text) text += el.Text + '\n'; return text; },'');
Als Nächstes erstelle ich eine HTTP-Anfrage an Diffbot. Weitere Informationen finden Sie in den Dokumenten .
let fields = 'summary'; let url = `https://nl.diffbot.com/v1/?fields=${fields}&token=${DIFFBOT_KEY}`; let body = [{ content:text, lang:'en', format:'plain text' }]; console.log('Passing text to Diffbot.'); let req = await fetch(url, { method:'POST', body:JSON.stringify(body), headers: { 'Content-Type':'application/json' } }); let result = await req.json();
Und das ist es. Als letzten Schritt gebe ich es einfach aus:
console.log(`Summary of PDF:\n${result[0].summary}`);
Angesichts der Quell-PDF lautet das Endergebnis:
Adobe has a vendor security review program that evaluates vendors that collect, store, process, transmit, or dispose of Adobe data outside of Adobe-controlled physical offices or data center locations. The VSR program includes requirements for vendors to follow when handling sensitive data and assigns a risk level score to vendors based on their compliance with Adobe standards. If a vendor fails the VSR program, Adobe holds discussions with the business owner to understand the details of the vendor's security practices and determine whether or not to continue working with them.
Mein dreiseitiges PDF besteht jetzt aus einem einfachen Absatz. Sie können sich vorstellen, wie nützlich dies für Organisationen mit Millionen von Dokumenten wäre. Kombinieren Sie dies mit anderen Diensten (wie der zuvor erwähnten Entitätsfunktion), und es erleichtert die Arbeit mit großen Bibliotheken erheblich.
Wenn Sie dies selbst überprüfen möchten, können Sie den gesamten Code hier abrufen: https://github.com/cfjedimaster/document-services-demos/tree/main/random_demos/extract_diffbot_summary . Wie gesagt, hier kann alles kostenlos getestet werden, also probieren Sie es aus und teilen Sie mir Ihre Meinung unten in einem Kommentar mit.