कैसे स्क्रैप की गई डेटा को एंटिटी रिज़ॉल्यूशन के साथ डुप्लिकेट रहित करें
Web Data Collection Specialist
TL;DR:
- चार लिस्टिंग पृष्ठों ने 71 रिकॉर्ड लौटाए जो 66 संस्थाओं में परिवर्तित हुए, जिनमें 5 असली डुप्लिकेट समूह शामिल हैं — वही पुस्तकें एक श्रेणी सूची और पृष्ठांकित सूची दोनों में प्रकट हो रही हैं।
- तुलना करने से पहले सामान्यीकृत करना वही है जो एक सटीक कुंजी को कार्यशील बनाता है: केस फोल्डिंग, यूनिकोड सामान्यीकरण और विराम चिह्न हटा देना एक शीर्षक के तीन रूपों को एक कुंजी में बदल देता है।
- ब्लॉकिंग मापनीय है, अस्पष्ट नहीं। 66 संस्थाएं 2,145 सभी जोड़ियों की तुलना करती हैं; एक चार-चरित्र ब्लॉक कुंजी इसे 48 ब्लॉकों के बीच 154 तक कम करती है, 92.8% की कमी।
- थ्रेशोल्ड से पहले स्कोरर चुनें। उसी शीर्षकों के जोड़े ने
ratioपर 87.8 औरtoken_set_ratioपर 100.0 अंक प्राप्त किए। - थ्रेशोल्ड बैंड को अपने डेटा पर मापा जा सकता है: असंबंधित लाइव शीर्षकों ने 63.4 पर पीक किया जबकि एक पुस्तक के तीन रूपों ने 93.5 और उससे अधिक अंक प्राप्त किए।
- एक एकल साफ सूची पर, सटीक कुंजियों ने हर डुप्लिकेट को पकड़ लिया और धुंधली मिलान ने 90 से ऊपर कुछ नहीं पाया — धुंधली अपनी जगह स्रोतों के बीच अर्जित करती है, न कि एक के भीतर।
- मल्टी-स्रोत रिकॉर्ड एकत्र करें जो Scrapeless मुफ्त योजना के साथ सामंजस्य बिठाते हैं।
एक साइट को स्क्रैप करें और डुप्लिकेट दुर्लभ होते हैं। एक श्रेणी सूची और एक पृष्ठांकित अनुक्रमणिका के माध्यम से उसी सूची को स्क्रैप करें, या दो खुदरा विक्रेताओं के बीच एक ही उत्पाद को मैच करें, और डुप्लिकेट इरादतन आते हैं — क्रॉल ने दो मार्गों द्वारा समान आइटम का दौरा किया और इसके पास जानने का कोई तरीका नहीं था।
संख्या हल करने का चरण है जो उन रिकॉर्ड को वस्तुओं में वापस बदलता है। यह निष्कर्षण के बाद और संग्रहण से पहले चलता है, और यह ज्यादातर सस्ते निर्णयों की एक श्रृंखला है: एक ही स्ट्रिंग के रूप में क्या गिना जाता है, एक ही रिकॉर्ड के रूप में क्या गिना जाता है, और कौन सा संस्करण जिंदा रहता है।
नीचे दिया गया प्रत्येक संख्या चार लाइव लिस्टिंग पृष्ठों से लिए गए 71 रिकॉर्ड के एक संग्रह से आती है।
Pipeline at a Glance
| चरण | प्रश्न | तंत्र | मापा गया परिणाम |
|---|---|---|---|
| सामान्यीकृत करें | क्या यह वही स्ट्रिंग है? | केस फोल्ड, NFKD, विराम चिह्न हटा दें | 71 रिकॉर्ड से 66 अलग कुंजियाँ |
| सटीक कुंजी | क्या यह वही रिकॉर्ड है? | सामान्यीकृत कुंजी द्वारा समूह बनाएं | 5 डुप्लिकेट समूह, 10 रिकॉर्ड से 5 |
| ब्लॉक | कौन से जोड़े तुलना करने योग्य हैं? | 4-चरित्र कुंजी उपसर्ग | 2,145 जोड़ियों से 154, 92.8% की कटौती |
| धुंधली | क्या यह वही चीज है, अलग तरीके से लिखा हुआ? | token_set_ratio |
सही मेल पर 93.5–100, असंबंधित पर 63.4 का छत |
| सर्वाइवशिप | कौन सा रिकॉर्ड जीतेगा? | क्षेत्र नियम, प्रूवेंस बनाए रखें | एक संस्था में seen_in और अवलोकित मूल्य |
प्रवाहित होता है सामान्यीकृत → सटीक कुंजी → ब्लॉक → धुंधली तुलना → विलय। प्रत्येक चरण पिछले एक से सस्ता होता है, इसलिए प्रत्येक एक उस काम को कम करने के लिए होता है जो अगला करने वाला है।
Stage 1: सामान्यीकृत करने से पहले तुलना करना
दो रिकॉर्ड जो समान उत्पाद का वर्णन करते हैं, दुर्लभ रूप से बाइट-आइडेंटिकल स्ट्रिंग्स ले जाते हैं। केस, उच्चारण और विराम चिह्न सभी चलाते हैं।
python
import re
import unicodedata
def norm(text):
text = unicodedata.normalize("NFKD", text or "").casefold()
text = re.sub(r"[^a-z0-9 ]+", " ", text)
return re.sub(r"\s+", " ", text).strip()
NFKD पास उस सापेक्ष से ज्यादा महत्वपूर्ण है जितना यह दिखता है। यूनिकोड सामान्यीकरण परिशिष्ट कई रूपों को परिभाषित करता है, और एक संगत विघटन वही है जो एक पूर्व-घटित é और एक बिना e के साथ संयोजक उच्चारण की तुलना समान बनाता है — दोनों वर्तनी दृश्य रूप से समान और बाइट में भिन्न हैं, जो बिल्कुल वही मामला है जो एक डुप्लिकेट का उत्पादन करता है जिसे कोई आउटपुट में नहीं देख सकता है।
केस फोल्डिंग, न कि लोअरकेसिंग, मिलानशील समकक्ष है, और सामान्यीकरण पर W3C वर्ण-आधारित नोट यह बताते हैं कि क्यों दोनों गैर-ASCII टेक्स्ट के लिए भिन्न हैं।
एकत्रित रिकॉर्ड पर:
text
[1] collected 71 records from 4 listing pages
raw distinct titles 66
normalised distinct titles 66
यहां समान है, क्योंकि यह सूची साफ है। यह जानने लायक है, न कि मान लेने के लिए — तुलना करने से आपको बताता है कि क्या सामान्यीकरण आपके डेटा पर किसी काम में आ रहा है इससे पहले कि आप इसके शीर्ष पर कुछ बनाएं।
Stage 2: सटीक कुंजी पर समूह बनाएं
एक सामान्यीकृत कुंजी के साथ, पहला पास एक समूह बनाना है, तुलना नहीं। यह O(n) है और यह हर डुप्लिकेट को पकड़ता है जो ठीक से सहमत है।
python
from collections import defaultdict
by_key = defaultdict(list)
for record in records:
by_key[norm(record["title"])].append(record)
dupe_groups = {k: v for k, v in by_key.items() if len(v) > 1}
text
[2] exact-key duplicates: 5 group(s), 10 records collapse to 5
Sharp Objects x2 ['mystery', 'catalogue1']
In a Dark, Dark Wood x2 ['mystery', 'catalogue2']
In Her Wake x2 ['catalogue2', 'thriller']
The Elephant Tree x2 ['catalogue2', 'thriller']
Behind Closed Doors x2 ['catalogue2', 'thriller']
नोट करें कि डुप्लिकेट कहां से आते हैं: प्रत्येक समूह दो विभिन्न लिस्टिंग पृष्ठों को कवर करता है। कोई एकल पृष्ठ एक डुप्लीकेट को नहीं रखता था। यही सामान्य आकर है — डुप्लिकेट क्रॉल की एक संपत्ति हैं, पृष्ठ की नहीं, इसलिए एक स्क्रैपर जो केवल कभी एक लिस्टिंग पढ़ता है, उन्हें नहीं देखेगा और एक स्क्रैपर जो चार पढ़ता है, उन्हें देखेगा।
जब भी पृष्ठ एक स्थिर पहचानकर्ता प्रकाशित करता है तो उसे कुंजी के रूप में उपयोग करें। एक उत्पाद आईडी, एक ISBN या एक मानक URL पथ एक शीर्षक से बेहतर होती है, क्योंकि शीर्षक विपणन की कॉपी होते हैं और उत्पाद के बिना बदलते हैं। प्रकाशित पहचानकर्ता योजनाएं ठीक उसी कारण के लिए मौजूद हैं ताकि स्वतंत्र पार्टियाँ पहचान पर सहमत हो सकें — ISBN URN नामस्थान विनिर्देशन पुस्तक-जगत का उदाहरण है, और एक स्क्रैप किया गया पृष्ठ जो एक को उजागर करता है पहले से ही आपके लिए मिलान समस्या को हल कर चुका है।
Stage 3: जोड़े की तुलना करने से पहले ब्लॉक करें
फज़ी तुलना जोड़ी के अनुसार होती है, और जोड़ी के अनुसार संख्या वर्णात्मक होती है। 66 संस्थाओं के लिए यह 2,145 तुलना है; 10,000 के लिए यह 50 मिलियन से थोड़ी कम होती है।
ब्लॉकिंग क्षेत्र को केवल उन रेकॉर्ड की तुलना करके काटती है जो पहले से ही कुछ सस्ता साझा करते हैं:
python
blocks = defaultdict(list)
for entity in merged:
blocks[norm(entity["title"])[:4]].append(entity)
blocked_pairs = sum(len(b) * (len(b) - 1) // 2 for b in blocks.values())
text
[4] 66 entities
all-pairs comparisons 2145
blocked on 4-char key 154 across 48 blocks
reduction 92.8%
वाणिज्य स्पष्ट है: एक रिकॉर्ड जिसका शीर्षक अलग शुरू होता है, उसकी कभी तुलना नहीं की जाती है, इसलिए एक ब्लॉक कुंजी जो बहुत आक्रामक होती है वास्तविक मेल को छुपा देती है। पहले चार अक्षरों पर ब्लॉकिंग एक जोड़ी को चूकने की संभावना बनाती है जैसे The Elephant Tree बनाम Elephant Tree क्योंकि लेख स्थानांतरित हो गया। सामान्य उत्तर हैं कि एक सॉर्ट की गई टोकन उपसर्ग, एक संख्या पहचानकर्ता, या एक साथ कई कुंजियों पर ब्लॉक करें और चयनित जोड़ों का संघ लें।
चरण 4: फज़ी मेलिंग, और जब इसकी आवश्यकता नहीं है
इस कैटलॉग पर फज़ी पास चलाने से एक ऐसा परिणाम मिला जिसकी ईमानदारी से रिपोर्ट करना उचित है:
text
[5] fuzzy near-duplicates above 90 (token_sort_ratio)
brute force 2145 pairs in 2.5 ms -> 0 candidate(s)
कुछ नहीं। सामान्यीकरण और सटीक समूह बनाने के बाद, एक साफ कैटलॉग में कोई भी निकट-प्रतिकृतियाँ नहीं रहीं। यहां एक फज़ी पास ऐसा कोड होगा जो कभी नहीं चलेगा।
फज़ी मेलिंग का स्थान तब मिलता है जब रिकॉर्ड उन स्रोतों से आते हैं जो शीर्षकों को अलग तरीके से फॉर्मेट करते हैं। एक वास्तविक शीर्षक को लेना और इसे तीन अलग-अलग सूचियों के अनुसार प्रस्तुत करना:
python
from rapidfuzz import fuzz
VARIANTS = [
"A Study in Scarlet (Sherlock Holmes #1)",
"A Study In Scarlet - Sherlock Holmes Book 1",
"A Study in Scarlet, Sherlock Holmes #1 [Paperback]",
]
keys = [norm(v) for v in VARIANTS]
print("distinct exact keys:", len(set(keys)))
for i in range(len(keys)):
for j in range(i + 1, len(keys)):
print(f"ratio {fuzz.ratio(keys[i], keys[j]):5.1f} | "
f"token_sort {fuzz.token_sort_ratio(keys[i], keys[j]):5.1f} | "
f"token_set {fuzz.token_set_ratio(keys[i], keys[j]):5.1f}")
text
distinct exact keys: 3
ratio 93.5 | token_sort 93.5 | token_set 100.0
ratio 87.8 | token_sort 87.8 | token_set 100.0
ratio 85.1 | token_sort 82.8 | token_set 93.5
एक पुस्तक के लिए तीन कुंजियाँ — सटीक-कुंजी चरण यहां मदद नहीं कर सकता। और स्कोरर उत्तर को उस सीमा से ज्यादा बदल देता है। ratio स्ट्रिंग्स की तुलना अनुक्रमों के रूप में करता है और [Paperback] उपसर्ग द्वारा नीचे खींचा जाता है; token_set_ratio टोकनों के सेट की तुलना करता है, इसलिए अतिरिक्त शब्दों की कोई कीमत नहीं होती और पहले दो भिन्नताएँ एक साफ 100 अंक प्राप्त करती हैं।
कई स्रोतों से रिकॉर्ड का पुनर्मिलान कर रहे हैं? Scrapeless मुफ्त योजना दूसरे कैटलॉग को इकट्ठा करने के लिए पर्याप्त अनुरोध कवर करती है जो प्रतिकृतियाँ उत्पन्न करती हैं।
अपने डेटा से थ्रेशोल्ड चुनना
एक थ्रेशोल्ड केवल मापी गई पृथक्करण के खिलाफ रक्षा योग्य है। दो संख्याएँ यहां इसे सीमाबद्ध करती हैं:
| माप | स्कोर |
|---|---|
दो वास्तव में अलग लाइव शीर्षकों के बीच सबसे अधिक token_sort_ratio |
63.4 |
एक पुस्तक के तीन रूपों के बीच सबसे कम token_set_ratio |
93.5 |
इन दोनों के बीच कुछ भी सेट को इस डेटा पर साफ़ रूप से अलग करता है। यह विधि सामान्यीकृत होती है: ज्ञात मेलों के एक नमूने और ज्ञात गैर-मेलों के एक नमूने को स्कोर करें, देखें कि वितरण कहां ओवरलैप करना बंद करता है, और थ्रेशोल्ड को गैप में रखें। किसी लेख से कॉपी की गई एकल वैश्विक संख्या किसी और के डेटा के बारे में एक अनुमान है।
जहां वितरण ओवरलैप करते हैं, ईमानदार उत्तर एक समीक्षा बैंड है - ऊपरी सीमा के ऊपर स्वचालित-मिलान, निचली सीमा के नीचे स्वचालित-रद्दीकरण, और बीच में जो आता है उसे कतार में रखें। सांख्यिकीय रिकॉर्ड लिंकन ने दशकों से इस समस्या का इस तरह से समाधान किया है, और यूएस जनगणना ब्यूरो का रिकॉर्ड-लिंकन अनुसंधान संभाव्य ढांचे के लिए मानक संदर्भ है।
चरण 5: जीवित रहना
दो रिकॉर्ड के समान होने का निर्णय लेने पर यह प्रश्न उठता है कि मर्ज की गई रिकॉर्ड क्या कहती है। हारने वाले को चुपचाप अस्वीकार करना उस साक्ष्य को फेंक देता है कि मेल हुआ था।
python
def survivor(group):
best = sorted(group, key=lambda r: (r["href"] is None, len(r["href"] or "")))[0]
return {**best,
"seen_in": sorted({g["source"] for g in group}),
"prices": sorted({g["price"] for g in group})}
text
[3] 71 records -> 66 entities
merged example: 'Sharp Objects' seen_in=['catalogue1', 'mystery'] prices=['£47.82']
उस मर्ज की गई रिकॉर्ड की दो विशेषताएँ महत्वपूर्ण हैं। seen_in उत्पत्ति को बनाए रखता है, इसलिए एक गलत मर्ज के बाद पता लगाना संभव है जबकि यह अदृश्य होता है। और prices एक सेट है न कि एकल मान: जब दो स्रोत असहमत होते हैं, तो असहमति ही दिलचस्प हिस्सा होती है, और इसे पहले क्रम में होने वाले रिकॉर्ड में संकुचित करना इसे नष्ट कर देता है।
फील्ड-स्तरीय नियम एक संपूर्ण रिकॉर्ड विजेता को हरा देते हैं। सबसे लंबा विवरण, सबसे हाल का टाइमस्टैम्प, सबसे पूर्ण रिकॉर्ड, सबसे उच्च-विश्वास स्रोत — क्षेत्र के अनुसार चुना गया न कि रिकॉर्ड के अनुसार — ही एक मर्ज को एक स्रोत की कमी विरासत में लेने से रोकता है।
यह पाइपलाइन में कहां बैठता है
डुप्लीकेशन परिवर्तन चरण में होता है, निष्कर्षण के बाद और लेखन से पहले। इसे पहले चलाना मतलब आपको अभी तक पार्स नहीं किए गए स्ट्रिंग्स को सामान्यीकृत करना है; इसे बाद में चलाने का मतलब है कि डुप्लिकेट पहले ही टेबल में हैं और सुधार एक माइग्रेशन बन जाता है।
समान उत्पादों को कई स्रोतों से इकट्ठा करना ही इस चरण को पहली जगह आवश्यक बनाता है — प्रतिस्पर्धात्मक मूल्य निर्धारण पाइपलाइन की बिल्कुल यही आकृति है, और यूनिवर्सल स्क्रैपिंग एपीआई वह है जो यह सुनिश्चित करता है कि रिकॉर्ड की आकृति संगत बनी रहे जब उन स्रोतों में से कोई क्लाइंट-साइड उत्पन्न होता है। मूल्य निर्धारण अतिरिक्त स्रोतों की लागत को सूचीबद्ध करता है।
निष्कर्ष
एंटीटिटी रिज़ॉल्यूशन एक महंगे चरण से पहले चार सस्ते चरण होते हैं। सामान्यीकरण यह निर्धारित करता है कि कौन सा स्ट्रिंग समान माना जाता है, सटीक समूहबद्धता हर चीज को पकड़ लेती है जो सहमत है — यहाँ 5 समूह और 10 रिकॉर्ड हैं — बाधा 92.8% जोड़े हटा देती है जिनकी तुलना करने की जरूरत नहीं है, और केवल वही जो जीवित रहते हैं, फजी स्कोरर तक पहुँचते हैं।
दो निष्कर्ष हैं जिन्हें अपने डेटा में ले जाना लायक है। स्कोरर थ्रेशोल्ड से अधिक महत्वपूर्ण है: समान जोड़े पर 87.8 बनाम 100.0। और किसी संख्या का चयन करने से पहले अंतर को मापें, क्योंकि 63.4 से 93.5 का अंतर जो यहाँ चयन को स्पष्ट बनाता है, इस कैटलॉग की एक विशेषता है, कोई स्थायी नहीं।
क्या आप एक से अधिक स्रोतों से रिकॉर्ड को सामंजस्य बिठाने के लिए तैयार हैं? सक्रिय Scrapeless मुफ्त योजना से शुरू करें और दूसरा कैटलॉग इकट्ठा करें जो डुप्लिकेट को स्पष्ट करता है।
अक्सर पूछे जाने वाले सवाल
प्रश्न: मैं स्क्रैप किए गए डेटा से डुप्लिकेट कैसे हटा सकता हूँ?
मुख्य क्षेत्र को सामान्यीकरण करें, उस पर समूह बनाएं, फिर प्रत्येक समूह को विलय करें। केस फोल्डिंग, यूनिकोड NFKD सामान्यीकरण और विराम चिह्न स्ट्रिपिंग दृश्य रूप से समान स्ट्रिंग्स को एक कुंजी में बदल देती है, और समूह बनाना O(n) है न कि जोड़े के अनुसार। ऊपर दिए गए 71 रिकॉर्ड में 10 रिकॉर्ड को बिना किसी समानता स्कोरिंग के 5 संस्थाओं में समेकित किया गया। केवल वही पाने के लिए फजी मेल खाने के लिए पहुँचें।
प्रश्न: एंटीटिटी रिज़ॉल्यूशन क्या है?
यह तय करना कि कौन से रिकॉर्ड एक ही वास्तविक दुनिया की चीज़ को संदर्भित करते हैं और उन्हें एक कैनोनिकल रिकॉर्ड में समेकित करना। डुप्लिकेशन एक ही स्रोत के भीतर एक समान संचालन है; यह शब्द आमतौर पर कठिन क्रॉस-स्रोत मामले के लिए आरक्षित होता है, जहाँ कोई साझा पहचानकर्ता नहीं होता है और निर्णय क्षेत्र की समानता से लेना होता है।
प्रश्न: बाधा क्या है और यह महत्वपूर्ण क्यों है?
केवल उन रिकॉर्ड की तुलना करना जो पहले से एक सस्ते कुंजी को साझा करते हैं, इसलिए जोड़े के चरण में सब कुछ चलाना नहीं पड़ता। 66 संस्थाएं 2,145 संभावित जोड़े होती हैं; एक चार-अक्षरीय प्रीफिक्स कुंजी ने इसे 154 तक कम कर दिया, 92.8% की कमी। लागत यह है कि जिन रिकॉर्ड की कुंजी भिन्न होती है, उनकी कभी तुलना नहीं की जाती, इसलिए एक ब्लॉक कुंजी जो बहुत तंग होती है, चुपचाप मेल खाने वाले को छुपा देती है।
प्रश्न: मुझे कौन सा फजी मेल खाने वाला स्कोरर उपयोग करना चाहिए?
token_set_ratio उन शीर्षकों के लिए जो विभिन्न स्रोतों से अतिरिक्त शब्द उठाते हैं, क्योंकि यह टोकन सेट की तुलना करता है और अतिरिक्त को अनदेखा करता है — इसने वही जोड़े पर 100.0 स्कोर किया जहां ratio ने 87.8 दिया। जब स्थिति और क्रम अर्थ रखते हैं, जैसे कोड या पते, तो ratio का उपयोग करें। चुनने से पहले अपने डेटा से ज्ञात मेल खाने वाले के खिलाफ दोनों का परीक्षण करें।
प्रश्न: मुझे कौन सा समानता थ्रेशोल्ड सेट करना चाहिए?
इसे मापेंRather than copy it. Score a sample of known matches and known non-matches and put the threshold where the distributions stop overlapping. Here the highest score between different books was 63.4 and the lowest among renderings of one book was 93.5, so anything in that band worked. Where the two overlap, auto-merge above, auto-reject below, and queue the middle for review.
प्रश्न: एक विलय के बाद कौन सा रिकॉर्ड जीवित रहना चाहिए?
प्रत्येक क्षेत्र के अनुसार चुनें, न कि प्रति रिकॉर्ड। सबसे लंबा विवरण लें, सबसे हाल की कीमत, सबसे पूर्ण पता लें, और उत्पत्ति बनाए रखें — ऊपर का विलयित इकाई seen_in को बनाए रखता है और अवलोकित कीमतों का पूरा सेट। स्रोत सूची को बनाए रखना बुरे विलय को ट्रेस करने योग्य बनाता है; हर अवलोकित कीमत को बनाए रखना स्रोतों के बीच असहमति को संरक्षित करता है, जो अक्सर वह संकेत होता है जिसकी आप तलाश कर रहे थे।
स्क्रैपलेस में, हम केवल सार्वजनिक रूप से उपलब्ध डेटा का उपयोग करते हैं, जबकि लागू कानूनों, विनियमों और वेबसाइट गोपनीयता नीतियों का सख्ती से अनुपालन करते हैं। इस ब्लॉग में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है और इसमें कोई अवैध या उल्लंघन करने वाली गतिविधियों को शामिल नहीं किया गया है। हम इस ब्लॉग या तृतीय-पक्ष लिंक से जानकारी के उपयोग के लिए सभी देयता को कोई गारंटी नहीं देते हैं और सभी देयता का खुलासा करते हैं। किसी भी स्क्रैपिंग गतिविधियों में संलग्न होने से पहले, अपने कानूनी सलाहकार से परामर्श करें और लक्ष्य वेबसाइट की सेवा की शर्तों की समीक्षा करें या आवश्यक अनुमतियाँ प्राप्त करें।



