Daftar Isi
Membersihkan data kotor dan menangani kasus edge
Data yang diambil dari Internet sering kali tidak konsisten atau tidak lengkap, sehingga menimbulkan tantangan dalam sebagian besar proyek scraping. Bagian ini menjelaskan strategi untuk menangani kasus-kasus khusus tersebut, termasuk data yang hilang, format data yang berbeda, dan entri duplikat.
Berikut beberapa strategi untuk menangani kasus edge
- Coba/Kecuali – Berguna untuk menangani kesalahan dengan baik.
- Penguraian bersyarat – Menerapkan logika kondisional untuk menganalisis data secara berbeda berdasarkan strukturnya.
- Kelas data – Gunakan kelas data Python untuk menyusun data Anda agar pembersihan dan manipulasi menjadi lebih mudah.
- Saluran data – Menerapkan saluran data untuk membersihkan, memvalidasi, dan mengubah data sebelum disimpan.
- Pembersihan selama analisis data – Lakukan pembersihan data sebagai bagian dari proses analisis data Anda.
Menerapkan kelas data untuk data terstruktur
Di bagian ini, kami menggunakan kelas data untuk menyusun data sisa kami guna memastikan konsistensi dan kemudahan manipulasi.
Sebelum kami membersihkan dan memproses data, kami menentukan impor yang diperlukan dari dataclasses
Modul dan atur milik kita Product
Kelas data. Kelas ini berfungsi sebagai cetak biru untuk menyusun data produk kami yang telah di-crack.
from dataclasses import dataclass, field, InitVar
@dataclass
class Product:
name: str = ""
price: InitVar(str) = ""
url: str = ""
price_gbp: float = field(init=False)
price_usd: float = field(init=False)
def __post_init__(self, price):
self.url = self.normalize_url()
self.price_gbp = self.clean_price(price)
self.price_usd = self.price_to_usd()
Di sini kita mengimpor dataclass
, field
Dan InitVar
dari itu dataclasses
Modul. Itu @dataclass
Dekorator secara otomatis menambahkan metode khusus ke dalamnya Product
kelas, seperti __init__
Dan __repr__
berdasarkan atribut kelasnya.
Kelas produk didefinisikan dengan beberapa atribut:
- Nama belakang: String yang mewakili nama produk.
- Harga: Variabel inisialisasi (
InitVar
), yang berisi harga sebagai string. Atribut ini tidak termasuk dalam kelas__init__
metode dan digunakan untuk pengolahan sebelum dibuang. - URL (URL): String yang mewakili URL produk.
- Harga_GBP: Float yang mewakili harga produk dalam GBP. Atribut ini tidak termasuk dalam kelas
__init__
metode dan diatur selama inisialisasi (init=False
). - Harga (Rp: Float yang mewakili harga produk dalam USD. Bagaimana
price_gbp
atribut ini tidak termasuk dalam kelas__init__
Metode dan diatur selama inisialisasi.
Penyiapan ini memberikan cara terstruktur untuk mengelola data produk, termasuk pembersihan dan konversi harga, normalisasi URL, dan banyak lagi.
Langkah selanjutnya mencakup penerapan metode di dalam Product
kelas untuk melakukan operasi ini.
Bersihkan harganya
Dengan kami Product
Setelah kelas data ditentukan, langkah selanjutnya adalah menerapkan metode untuk membersihkan data harga. Kami membersihkan string harga dengan menghapus karakter yang tidak perlu seperti simbol mata uang (“£”) dan indikator harga jual (“Harga jual£”, “Harga jualDari £”).
Tentukan metode clean_price
dalam Product
Kelas yang mengambil string harga, menghapus semua karakter non-numerik, dan mengembalikan harga yang telah disanitasi sebagai float.
def clean_price(self, price_string: str):
price_string = price_string.strip()
price_string = price_string.replace("Sale price£", "")
price_string = price_string.replace("Sale priceFrom £", "")
price_string = price_string.replace("£", "")
if price_string == "":
return 0.0
return float(price_string)
- Metode ini pertama-tama menghilangkan spasi di awal atau akhir dari string harga
- Semua contoh “Harga Jual£” dan “Harga JualDari £” kemudian dihapus dari string
- Simbol “£” kemudian akan dihapus.
Jika string yang dihasilkan kosong, “0,0” dikembalikan, menunjukkan bahwa harga tidak ada atau tidak tersedia. Jika tidak, string yang dibersihkan akan diubah menjadi float dan dikembalikan.
Konversikan harganya
Setelah kami membersihkan harga, kami perlu mengonversikannya dari GBP ke USD untuk menstandarkan mata uang di seluruh kumpulan data kami, terutama saat menangani data internasional.
def price_to_usd(self):
return self.price_gbp * 1.28
Metode ini mengalikan harga GBP yang disesuaikan dengan kurs konversi (1.28 dalam contoh ini) untuk menghitung harga dalam USD. Nilai konversi ini dapat diperbarui secara dinamis berdasarkan nilai tukar saat ini.
Normalisasikan URL
Kasus umum lainnya saat mengambil data adalah format URL yang tidak konsisten. Beberapa URL dapat berupa jalur relatif sementara yang lain merupakan URL absolut. Normalisasi URL memastikan bahwa URL diformat secara konsisten, sehingga lebih mudah digunakan.
Kita akan mendefinisikan a normalize_url
metode di dalam Product
Kelas yang memeriksa apakah URL dimulai dengan http:// atau https://. Jika tidak, maka diberi “awalan”http://example.com“ ke URL.
def normalize_url(self):
if self.url == "":
return "missing"
if not self.url.startswith("http://") and not self.url.startswith("https://"):
return "http://example.com" + self.url
return self.url
- Cara ini pertama-tama memeriksa apakah URL tersebut kosong. Jika hal ini terjadi, “hilang” dikembalikan.
- Ia kemudian memeriksa apakah URL tidak dimulai dengan “http://” atau “https://”.
- Dalam hal ini, URL diawali dengan “http://example.com” untuk memastikan formatnya valid.
- Jika URL sudah diawali dengan "http://" atau "https://", URL dikembalikan tanpa perubahan.
Uji kelas data produk
Terakhir, uji kelas data Produk dengan beberapa data sampel.
# Sample scraped data
scraped_products = (
{"name": "Delicious Chocolate", "price": "Sale priceFrom £1.99", "url": "/delicious-chocolate"},
{"name": "Yummy Cookies", "price": "£2.50", "url": "http://example.com/yummy-cookies"},
{"name": "Tasty Candy", "price": "Sale price£0.99", "url": "/apple-pies"}
)
# Process and clean the data
processed_products = (Product(name=product("name"), price=product("price"), url=product("url")) for product in scraped_products)
# Display the processed products
for product in processed_products:
print(f"Name: {product.name}, GBP_Price: £{product.price_gbp}, USD_Price: ${product.price_usd}, URL: {product.url}")
Kode ini membuat daftar kamus yang mewakili beberapa data produk yang diekstrak. Ia kemudian mengulangi daftar ini dan membuat a Product
Contoh untuk setiap kamus dan tambahkan ke processed_products
Daftar.
Akhirnya itu mengulangi processed_products
Cantumkan dan cetak nama, harga GBP, harga USD dan URL masing-masing produk.
Name: Delicious Chocolate, GBP_Price: £1.99, USD_Price: $2.5472,URL: http://example.com/delicious-chocolate
Name: Yummy Cookies, GBP_Price: £2.5, USD_Price: $3.2,URL: http://example.com/yummy-cookies
Name: Tasty Candy, GBP_Price: £0.99, USD_Price: $1.2672,URL: http://example.com/apple-pies
Tindakan ini memverifikasi bahwa kelas data produk membersihkan dan memproses data yang dihapus dengan benar.
Kode kelas data lengkap
Berikut kode lengkapnya Product
Contoh kelas data.
from dataclasses import dataclass, field, InitVar
@dataclass
class Product:
name: str = ""
price: InitVar(str) = ""
url: str = ""
price_gbp: float = field(init=False)
price_usd: float = field(init=False)
def __post_init__(self, price):
self.url = self.normalize_url()
self.price_gbp = self.clean_price(price)
self.price_usd = self.price_to_usd()
def clean_price(self, price_string: str):
price_string = price_string.strip()
price_string = price_string.replace("Sale price£", "")
price_string = price_string.replace("Sale priceFrom £", "")
price_string = price_string.replace("£", "")
if price_string == "":
return 0.0
return float(price_string)
def normalize_url(self):
if self.url == "":
return "missing"
if not self.url.startswith("http://") and not self.url.startswith("https://"):
return "http://example.com" + self.url
return self.url
def price_to_usd(self):
return self.price_gbp * 1.28
# Sample scraped data
sample_products = (
{'name': 'Artisanal Chocolate', 'price': 'Sale price£2.99', 'url': '/artisanal-chocolate'},
{'name': 'Gourmet Cookies', 'price': '£5.50', 'url': 'http://example.com/gourmet-cookies'},
{'name': 'Artisanal Chocolate', 'price': 'Sale price£2.99', 'url': '/artisanal-chocolate'},
{"name": "Delicious Chocolate", "price": "Sale priceFrom £1.99", "url": "/delicious-chocolate"},
{"name": "Yummy Cookies", "price": "£2.50", "url": "http://example.com/yummy-cookies"},
{"name": "Tasty Candy", "price": "Sale price£0.99", "url": "/apple-pies"}
)
# Process and clean the data
processed_products = ()
for product in scraped_products:
prod = Product(name=product("name"),
price=product("price"),
url=product("url"))
processed_products.append(prod)
# Display the processed products
for product in processed_products:
print(f"Name: {product.name}, GBP_Price: £{product.price_gbp}, USD_Price: ${product.price_usd},URL: {product.url}")
Memproses dan menyimpan data yang tergores dengan saluran data
Setelah membersihkan data menggunakan pendekatan terstruktur Kelas Data Produk, kami melanjutkan ke langkah penting berikutnya: memproses dan menyimpan data ini secara efisien.
Pada fase ini, pipeline data memainkan peran sentral, memungkinkan kita memproses data secara sistematis sebelum menyimpannya. Operasi dalam saluran data kami meliputi:
- Cek duplikat: Periksa apakah suatu elemen sudah ada dalam kumpulan data untuk menghindari redundansi.
- Manajemen antrian data: Menyimpan sementara data yang diproses sebelum menyimpan dan mengelola aliran dan volume.
- Penyimpanan data berkala: Menyimpan data ke file CSV secara berkala atau berdasarkan pemicu tertentu.
Menyiapkan kelas ProductDataPipeline
Pertama mari kita definisikan struktur kita ProductDataPipeline
Kelas, dengan penekanan pada inisialisasi dan metode dasar yang mendukung operasi di atas:
import csv
from dataclasses import asdict, fields
class ProductDataPipeline:
def __init__(self, csv_filename='product_data.csv', storage_queue_limit=10):
self.names_seen = set()
self.storage_queue = ()
self.storage_queue_limit = storage_queue_limit
self.csv_filename = csv_filename
self.csv_file_open = False
def save_to_csv(self):
pass
def clean_raw_product(self, scraped_data):
pass
def is_duplicate(self, product_data):
pass
def add_product(self, scraped_data):
pass
def close_pipeline(self):
pass
Itu __init__
Metode ini menyiapkan kondisi awal, termasuk kumpulan untuk melacak nama produk yang terlihat (untuk memeriksa duplikat), antrean untuk menyimpan produk sementara, dan konfigurasi untuk keluaran file CSV.
Bersihkan data produk mentah
Sebelum suatu produk dapat ditambahkan ke antrean pemrosesan kami, produk tersebut harus dibersihkan dan disusun terlebih dahulu. Itu clean_raw_product
Metode ini mencapai hal ini dengan mengubah data mentah yang tergores menjadi sebuah instance Product
Kelas data untuk memastikan bahwa data kami sesuai dengan struktur dan tipe yang diharapkan.
def clean_raw_product(self, scraped_data):
cleaned_data = {
"name": scraped_data.get("name", ""),
"price": scraped_data.get("price", ""),
"url": scraped_data.get("url", "")
}
return Product(**cleaned_data)
Setelah data produk mentah dibersihkan, data tersebut harus diperiksa apakah ada duplikatnya dan, jika unik, ditambahkan ke antrean pemrosesan. Ini dikelola oleh add_product
Dan is_duplicate
metode atau
Tambahkan produk dan periksa duplikatnya
Itu is_duplicate()
Fungsi memeriksa apakah suatu produk sudah ada di names_seen
Daftar. Jika ya, pesan akan dicetak dan dikembalikan True
, menunjukkan bahwa produk tersebut adalah duplikat. Jika tidak, nama produk ditambahkan names_seen
daftar dan kembali False
.
def is_duplicate(self, product_data):
if product_data.name in self.names_seen:
print(f"Duplicate item found: {product_data.name}. Item dropped.")
return True
self.names_seen.add(product_data.name)
return False
Itu add_product
Metode ini pertama-tama membersihkan data yang dihapus dan membuat a Product
Obyek. Ia kemudian memeriksa apakah produk tersebut duplikat is_duplicate
Metode. Jika bukan duplikat, produk akan ditambahkan ke dalamnya storage_queue
. Terakhir, ia memeriksa apakah antrean penyimpanan telah mencapai batasnya dan, jika ya, akan memanggilnya save_to_csv
Metode untuk menyimpan produk dalam file CSV.
def add_product(self, scraped_data):
product = self.clean_raw_product(scraped_data)
if not self.is_duplicate(product):
self.storage_queue.append(product)
if len(self.storage_queue) >= self.storage_queue_limit:
self.save_to_csv()
Penyimpanan data secara teratur dalam format CSV
Itu save_to_csv
Metode ini diaktifkan ketika antrean penyimpanan mencapai batasnya atau ketika alur ditutup, sehingga memastikan persistensi data.
def save_to_csv(self):
if not self.storage_queue:
return
self.csv_file_open = True
with open(self.csv_filename, mode='a', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=(field.name for field in fields(self.storage_queue(0))))
if csvfile.tell() == 0:
writer.writeheader()
for product in self.storage_queue:
writer.writerow(asdict(product))
self.storage_queue.clear()
self.csv_file_open = False
Itu save_to_csv
Metode ini dirancang untuk dieksekusi ketika antrian memori tidak kosong. Ini menandai file CSV sebagai terbuka (untuk mengelola akses bersamaan), kemudian mengulangi setiap produk dalam antrian dan membuat cerita bersambung ke dalam kamus (menggunakan asdict
) dan tulis ke file CSV.
File CSV terbuka dalam mode penambahan (a
), yang memungkinkan data ditambahkan tanpa menimpa informasi yang sudah ada. Metode ini memeriksa apakah file telah dibuat ulang dan menulis header kolom yang sesuai.
Setelah menyimpan produk yang antri, antrean dikosongkan dan file CSV ditandai sebagai ditutup, siap untuk kumpulan data berikutnya.
Menutup pipa
Dipastikan tidak ada data yang belum disimpan close_pipeline
Metode ini menangani aliran data akhir ke dalam file CSV.
def close_pipeline(self):
if self.storage_queue:
self.save_to_csv()
Menguji saluran data
Untuk menunjukkan keefektifan kami ProductDataPipeline
Mari kita simulasikan proses penambahan beberapa produk, termasuk duplikat, untuk melihat bagaimana saluran kami mengelola pembersihan data, deteksi duplikat, dan penyimpanan CSV.
data_pipeline = ProductDataPipeline(csv_filename='product_data.csv', storage_queue_limit=3)
# Sample scraped data to add to our pipeline
sample_products = (
{'name': 'Artisanal Chocolate', 'price': 'Sale price£2.99', 'url': '/artisanal-chocolate'},
{'name': 'Gourmet Cookies', 'price': '£5.50', 'url': 'http://example.com/gourmet-cookies'},
{'name': 'Artisanal Chocolate', 'price': 'Sale price£2.99', 'url': '/artisanal-chocolate'},
{"name": "Delicious Chocolate", "price": "Sale priceFrom £1.99", "url": "/delicious-chocolate"},
{"name": "Yummy Cookies", "price": "£2.50", "url": "http://example.com/yummy-cookies"},
{"name": "Tasty Candy", "price": "Sale price£0.99", "url": "/apple-pies"}
)
# Add each product to the pipeline
for product in sample_products:
data_pipeline.add_product(product)
# Ensure all remaining data in the pipeline gets saved to the CSV file
data_pipeline.close_pipeline()
print("Data processing complete. Check the product_data.csv file for output.")
Skrip pengujian ini menginisialisasi ProductDataPipeline
dengan nama file CSV tertentu dan batas antrian penyimpanan. Kami kemudian akan mencoba menambahkan tiga produk, termasuk duplikatnya, untuk melihat bagaimana saluran kami menanganinya.
Itu close_pipeline
Metode ini dipanggil terakhir untuk memastikan bahwa semua data ditulis ke file CSV. Hal ini menunjukkan kemampuan pipeline untuk mengelola data secara end-to-end.
Kode saluran data lengkap
Berikut kode lengkapnya ProductDataPipeline
Kelas yang mengintegrasikan semua langkah yang disebutkan dalam artikel ini:
import csv
from dataclasses import asdict, fields
class ProductDataPipeline:
def __init__(self, csv_filename='product_data.csv', storage_queue_limit=10):
self.names_seen = set()
self.storage_queue = ()
self.storage_queue_limit = storage_queue_limit
self.csv_filename = csv_filename
self.csv_file_open = False
def save_to_csv(self):
if not self.storage_queue:
return
self.csv_file_open = True
with open(self.csv_filename, mode='a', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=(field.name for field in fields(self.storage_queue(0))))
if csvfile.tell() == 0:
writer.writeheader()
for product in self.storage_queue:
writer.writerow(asdict(product))
self.storage_queue.clear()
self.csv_file_open = False
def clean_raw_product(self, scraped_data):
cleaned_data = {
"name": scraped_data.get("name", ""),
"price": scraped_data.get("price", ""),
"url": scraped_data.get("url", "")
}
return Product(**cleaned_data)
def is_duplicate(self, product_data):
if product_data.name in self.names_seen:
print(f"Duplicate item found: {product_data.name}. Item dropped.")
return True
self.names_seen.add(product_data.name)
return False
def add_product(self, scraped_data):
product = self.clean_raw_product(scraped_data)
if not self.is_duplicate(product):
self.storage_queue.append(product)
if len(self.storage_queue) >= self.storage_queue_limit:
self.save_to_csv()
def close_pipeline(self):
if self.storage_queue:
self.save_to_csv()
data_pipeline = ProductDataPipeline(csv_filename='product_data.csv', storage_queue_limit=3)
# Sample scraped data to add to our pipeline
sample_products = (
{'name': 'Artisanal Chocolate', 'price': 'Sale price£2.99', 'url': '/artisanal-chocolate'},
{'name': 'Gourmet Cookies', 'price': '£5.50', 'url': 'http://example.com/gourmet-cookies'},
{'name': 'Artisanal Chocolate', 'price': 'Sale price£2.99', 'url': '/artisanal-chocolate'},
{"name": "Delicious Chocolate", "price": "Sale priceFrom £1.99", "url": "/delicious-chocolate"},
{"name": "Yummy Cookies", "price": "£2.50", "url": "http://example.com/yummy-cookies"},
{"name": "Tasty Candy", "price": "Sale price£0.99", "url": "/apple-pies"}
)
# Add each product to the pipeline
for product in sample_products:
data_pipeline.add_product(product)
# Ensure all remaining data in the pipeline gets saved to the CSV file
data_pipeline.close_pipeline()
print("Data processing complete. Check the product_data.csv file for output.")
Terus belajar
Dalam artikel ini, kami telah memberikan panduan komprehensif untuk membersihkan dan menyusun data tergores menggunakan kelas data dan saluran data. Dengan mengikuti teknik ini, Anda dapat memastikan bahwa data yang Anda ambil akurat, konsisten, dan siap untuk dianalisis.
Jika Anda ingin mengambil data dalam jumlah besar tanpa diblokir, Anda harus menggunakan ScraperAPI. Ini menyediakan API sederhana yang memungkinkan Anda mendapatkan respons HTML yang dirender sepenuhnya, termasuk respons dari situs web dinamis.
Sampai jumpa lagi, selamat menggores!