Scraper'ınız gece boyunca çalışıp bitti ve CSV dosyası ilk bakışta sorunsuz görünüyor. Sonra fiyatların ortalamasını almak istiyorsunuz ve pandas buna izin vermiyor, çünkü her fiyat £51.77 gibi bir metin. Aynı kitap iki kez geçiyor, çünkü hem katalog sayfasında hem de bir kategori sayfasında duruyordu. Bir başlık Shakespeare’s diye okunuyor, yedi kitap "Default" adlı bir kategoride görünüyor ve bir satırda hiç açıklama yok. Bunların hiçbiri scraper'daki bir hata değil. Ham web verisi böyle görünür ve ondan grafik çizilmeden önce bir temizleme turundan geçmesi gerekir.
Bu rehber o turu, kazıma alıştırması için kurulmuş bir deneme sitesi olan books.toscrape.com üzerinden toplanmış gerçek bir veri setiyle ve pandas ile adım adım anlatıyor. Metin ve Unicode onarımını, eksik değerleri, fiyat metinlerinin sayıya çevrilmesini, metnin içine gizlenmiş diğer sayıları, tarihleri, kategori etiketlerini, yinelenen kayıtları, aykırı değerleri, doğrulama kontrollerini ve sonucun kaydedilmesini ele alıyoruz. Betiğin tamamını 28 Eylül 2026'da pandas 3.0.6, Requests 2.34.2, beautifulsoup4 4.15.0, pyarrow 25.0.1 ve Python 3.13 ile çalıştırdık.
Kazınan veri için "temiz" ne demek?
Temiz veride her gerçek nesne için bir satır, her sütun için bir tür ve her değer için tek bir yazım vardır. Fiyat sütununda para birimi işaretli metin değil, sayı durur. İki kez kazınan bir kitap bir kez görünür. Eksik bir açıklama, dolu gibi görünen boş bir dize olarak değil, eksik olarak kaydedilir. "Default" adlı bir kategori, analiste bu etiketin hiçbir bilgi taşımadığını söyleyen bir ad alır.
Temiz olmak, her ayrıntıda eksiksiz ya da doğru olmak anlamına gelmez. Sayfada hiç bulunmayan bir açıklamayı geri getiremezsiniz; bir temizleme betiği de onu uydurmamalıdır. Betiğin işi her sorunu görünür, kalan her değeri güvenilir kılmaktır. Böylece bir sonraki adım (bir grafik, bir model, bir fiyat uyarısı) ayrıştırma kazalarıyla değil, gerçeklerle çalışır.
Kazınan veri neden dağınıktır?
Web sayfaları insanlar için yazılır ve scraper, göze hitap eden kısımları okur. Dağınıklığın büyük bölümü birkaç kaynaktan gelir:
- Her şey metindir. HTML tür taşımaz.
£51.77,In stock (22 available)veThree(yıldız puanını gösteren bir CSS sınıfı) gibi değerlerin hepsi dize olarak gelir. - Kodlama tahminleri yanılır. books.toscrape.com, karakter kümesi belirtmeden
Content-Type: text/htmlgönderir. Bu durumda Requests eski HTTP/1.1 kuralını izler ve sayfayı ISO-8859-1 olarak çözer (Requests belgelerinde kodlamalar); UTF-8 sterlin işareti de£olur. Mekanizmayı Python Unicode kodlama hataları yazısında anlattık. - Aynı ürüne birden çok yoldan ulaşılır. Bir ürün katalogda, kendi kategorisinde ve bazen bir "yeni" ya da "indirim" listesinde yer alır. Bunlardan birden fazlasını tararsanız onu iki kez toplarsınız. Çalıştırmalar arasında kayan sayfalandırma da aynı sonucu doğurur.
- Şablonlar farklıdır. Bin sayfadan birinde, diğerlerinde bulunan bir blok eksiktir ve seçiciniz
Nonedöndürür. - Sitenin kendi verisi kusurludur. Yer tutucu kategoriler, fazladan boşluklar ve "...more" kuyrukları kaynağın parçasıdır, sizin hatanız değil.
Bir temizleme turu nasıl işler?
Sıra önemlidir, çünkü her adım bir öncekine dayanır:
- Ham veriyi saklayın. Scraper'ın topladığını olduğu gibi
books_raw.csvdosyasına kaydedin. Temizleme bir kopya üzerinde çalışır; böylece siteyi yeniden kazımadan temizlemeyi yeni kurallarla tekrar çalıştırabilirsiniz. - Metni normalleştirin. İki uçtaki boşlukları kırpın, boş dizeleri eksik değere çevirin, bozuk kodlamayı onarın ve Unicode normalleştirmesi uygulayın.
- Eksik değerleri sayın. Bunları bir rapora yazın ve işaret sütunları ekleyin; tahminle doldurmayın.
- Fiyatları dönüştürün. Para birimi simgesini ayrı bir sütuna alın, geri kalanını ondalık sayıya çevirin.
- Diğer sayıları çıkarın.
In stock (22 available)içinden stok adedini, sözcüklerden puanı, metinden yorum sayısını alın. - Tarihleri ayrıştırın ve saat dilimi bilgisi taşıyan zaman damgalarına çevirin.
- Kategorileri normalleştirin; tek bir kavramın tek bir etiketi olsun.
- Yinelenen kayıtları ayıklayın; bunu türler doğru hâle geldikten sonra, değişmeyen bir anahtara göre yapın.
- Aykırı değerleri işaretleyin; açıklayabileceğiniz basit bir kural kullanın.
- Doğrulayın ve kaydedin. Bir kontrol başarısız olursa durun; olmazsa CSV ve Parquet yazın.
Tekilleştirme bilerek sona yakın bir yerde duruyor. Yalnızca sondaki bir boşlukla ya da yanlış çözülmüş bir kesme işaretiyle ayrılan iki satır aynı kitaptır ve drop_duplicates bunu ancak 2. ile 7. adımlardan sonra görebilir.
Ham ve temiz değerler yan yana
Aşağıda kazımadan gelen gerçek değerleri ve betiğin onları neye dönüştürdüğünü görüyorsunuz:
| Sütun | Ham değer | Sorun | Temiz değer | pandas aracı |
|---|---|---|---|---|
price | £51.77 | Metin, bozuk kodlama | 51.77 (float64) + currency = £ | str.extract, pd.to_numeric |
availability | In stock (22 available) | Cümlenin içinde sayı | in_stock = 22 (Int64) | str.extract |
rating | Three | Sayı değil, sözcük | 3 (Int64) | map |
title | Shakespeare’s Globe | Bozuk karakter (mojibake) | Shakespeare’s Globe | encode/decode, unicodedata |
category | Default | Yer tutucu etiket | Uncategorized (category) | replace, astype("category") |
description | … and their affair begins. ...more | Metne karışmış sayfa öğesi | … and their affair begins. | str.removesuffix |
scraped_at | 2026-09-28T14:51:11+00:00 | Dize | datetime64[us, UTC] | pd.to_datetime |
upc | iki satırda aynı kod | Yinelenen kitap | tek satır, en yeni kopya | drop_duplicates |
pandas 3.0'da neler değişti?
pandas 3.0.0, 21 Ocak 2026'da yayımlandı; güncel sürüm 3.0.6. pandas 3.0 sürüm notlarındaki üç değişiklik temizleme kodunu doğrudan etkiler:
- Metinler kendi türüne kavuştu. Metin sütunları artık
objectyerinestrolarak algılanıyor ve bu sütunlardaki eksik değerler, diğer varsayılan türlerde olduğu gibiNaN. Betiğin metin sütunlarınıselect_dtypes(include=["object", "string"])ile seçmesinin nedeni bu: hem pandas 2'de hem pandas 3'te çalışır. - Copy-on-Write tek çalışma biçimi oldu.
df["price"][0] = 10gibi zincirleme atama artıkdfnesnesini değiştirmiyor; pandas 3.0.6 yalnızca birChainedAssignmentErroruyarısı basıyor. Bunun yerinedf.loc[0, "price"] = 10kullanın. EskiSettingWithCopyWarningkaldırıldı; onu susturmak için yazılan savunma amaçlı.copy()çağrılarına da artık gerek yok. - Ayrıştırılan tarihler varsayılan olarak mikrosaniye çözünürlüğünde. Dizelere uygulanan
pd.to_datetimeartık nanosaniye yerinedatetime64[us]döndürüyor; aşağıdaki çıktıda gördüğünüz de bu.
pandas 3 ayrıca Python 3.11 veya daha yenisini gerektiriyor. pd.to_numeric yalnızca errors="raise" ya da errors="coerce" kabul ediyor (to_numeric başvurusu); errors="ignore" geçen eski kod parçaları hata verir.
Betiğin tamamı: books.toscrape.com'u kazıyın ve temizleyin
Kütüphaneleri bir sanal ortama kurun. Parquet dosyası için pyarrow gerekir:
python -m venv .venv
.venv/bin/pip install pandas requests beautifulsoup4 pyarrow # Windows: .venv\Scripts\pipScraper iki katalog sayfasını ve iki kategori sayfasını (Poetry ve Classics) okur; toplam 78 kitap eder. İstekler arasında bir saniye bekler ve 429 ile 5xx yanıtlarında otomatik olarak yeniden dener. HTML ayrıştırma, BeautifulSoup rehberimizdeki yaklaşımı izler. İkinci çalıştırma books_raw.csv dosyasını okur ve yalnızca temizlemeyi tekrarlar.
"""books.toscrape.com'u kazı, ardından sonucu pandas ile temizle."""
import time
import unicodedata
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import urljoin
import pandas as pd
import requests
from bs4 import BeautifulSoup
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
BASE = "https://books.toscrape.com/"
START_URLS = [
BASE + "catalogue/page-1.html", # ana katalog
BASE + "catalogue/category/books/poetry_23/index.html", # bir kategori
BASE + "catalogue/category/books/classics_6/index.html", # bir kategori daha
]
MAX_PAGES = 2 # başlangıç URL'si başına liste sayfası
DELAY = 1.0 # istekler arasındaki saniye
RAW_FILE = Path("books_raw.csv")
session = requests.Session()
session.headers["User-Agent"] = "pandas-cleaning-tutorial/1.0 (contact: you@example.com)"
retry = Retry(total=4, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504])
session.mount("https://", HTTPAdapter(max_retries=retry))
# session.proxies = {"https": "http://user:pass@pr.proxynet.io:8000"}
def get(url):
time.sleep(DELAY)
resp = session.get(url, timeout=30)
resp.raise_for_status()
return resp.text # bilerek Requests'in tahminiyle çözülüyor: bkz. adım 2
def parse_book(url):
soup = BeautifulSoup(get(url), "html.parser")
table = {th.get_text(): td.get_text() for th, td in
zip(soup.select("table.table th"), soup.select("table.table td"))}
desc = soup.select_one("#product_description + p")
return {
"url": url,
"title": soup.h1.get_text(),
"category": soup.select("ul.breadcrumb a")[-1].get_text(),
"price": table.get("Price (incl. tax)"),
"availability": table.get("Availability"),
"rating": soup.select_one("p.star-rating")["class"][1],
"upc": table.get("UPC"),
"reviews": table.get("Number of reviews"),
"description": desc.get_text() if desc else None,
"scraped_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}
def scrape():
rows = []
for start in START_URLS:
url = start
for _ in range(MAX_PAGES):
soup = BeautifulSoup(get(url), "html.parser")
for a in soup.select("article.product_pod h3 a"):
rows.append(parse_book(urljoin(url, a["href"])))
nxt = soup.select_one("li.next a")
if not nxt:
break
url = urljoin(url, nxt["href"])
df = pd.DataFrame(rows)
df.to_csv(RAW_FILE, index=False, encoding="utf-8")
return df
def clean(raw):
df = raw.copy()
report = {"raw_rows": len(df)}
# 1. Her metin sütununda boşlukları kırp; boş dizeler eksik değer olur
text_cols = df.select_dtypes(include=["object", "string"]).columns
for col in text_cols:
df[col] = df[col].str.strip().replace("", pd.NA)
# 2. Unicode: bozuk karakterleri (mojibake) onar, sonra NFKC'ye normalleştir
def fix_text(s):
if pd.isna(s):
return s
try:
s = s.encode("latin-1").decode("utf-8") # "£" -> "£", "é" -> "é"
except (UnicodeEncodeError, UnicodeDecodeError):
pass # zaten doğru
return unicodedata.normalize("NFKC", s)
for col in ["title", "category", "description", "price"]:
df[col] = df[col].map(fix_text)
# 3. Eksik değerler: say, işaretle, uydurma
report["missing_before"] = df.isna().sum()[lambda s: s > 0].to_dict()
df["description"] = df["description"].str.removesuffix("...more").str.strip()
df["has_description"] = df["description"].notna()
# 4. Fiyat metinlerini sayıya çevir
df["currency"] = df["price"].str.extract(r"([£$€])", expand=False)
df["price"] = pd.to_numeric(
df["price"].str.replace(r"[^\d.]", "", regex=True), errors="coerce"
)
# 5. Metnin içine gizlenmiş diğer sayılar
df["in_stock"] = pd.to_numeric(
df["availability"].str.extract(r"(\d+)\s+available", expand=False),
errors="coerce",
).astype("Int64")
df["rating"] = df["rating"].map(
{"One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5}
).astype("Int64")
df["reviews"] = pd.to_numeric(df["reviews"], errors="coerce").astype("Int64")
# 6. Tarihler
df["scraped_at"] = pd.to_datetime(df["scraped_at"], utc=True, errors="coerce")
# 7. Kategoriler: her değere tek yazım, anlamsız etiketler birleştirilir
df["category"] = (df["category"]
.replace({"Default": "Uncategorized",
"Add a comment": "Uncategorized"})
.astype("category"))
# 8. Yinelenen kayıtlar: aynı UPC = aynı kitap; en yeni kopya kalır
report["duplicate_rows"] = int(df.duplicated("upc").sum())
df = (df.sort_values("scraped_at")
.drop_duplicates("upc", keep="last")
.reset_index(drop=True))
# 9. Aykırı değerler: işaretle, silme
q1, q3 = df["price"].quantile([0.25, 0.75])
iqr = q3 - q1
df["price_outlier"] = ~df["price"].between(q1 - 1.5 * iqr, q3 + 1.5 * iqr)
report["price_outliers"] = int(df["price_outlier"].sum())
df = df.drop(columns=["availability"])
report["clean_rows"] = len(df)
return df, report
def validate(df):
problems = []
if df["upc"].duplicated().any():
problems.append("duplicate UPCs")
if df["price"].isna().any():
problems.append("prices that did not parse")
if not df["price"].dropna().between(0, 1000).all():
problems.append("prices outside 0-1000")
if not df["rating"].between(1, 5).fillna(False).all():
problems.append("ratings missing or outside 1-5")
if df["title"].isna().any():
problems.append("rows without a title")
if problems:
raise ValueError("Validation failed: " + ", ".join(problems))
if __name__ == "__main__":
raw = pd.read_csv(RAW_FILE) if RAW_FILE.exists() else scrape()
clean_df, report = clean(raw)
validate(clean_df)
clean_df.to_csv("books_clean.csv", index=False, encoding="utf-8-sig")
clean_df.to_parquet("books_clean.parquet", index=False)
print(report)
print(clean_df.dtypes)
print(clean_df[["title", "category", "price", "rating", "in_stock"]].head())İlk çalıştırma yaklaşık iki dakika sürdü; bunun büyük kısmı nezaket gereği eklenen bekleme. Betiğin bastığı rapor satırı:
{'raw_rows': 78, 'missing_before': {'description': 1}, 'duplicate_rows': 5, 'price_outliers': 0, 'clean_rows': 73}Beş Poetry kitabı hem katalog sayfalarında hem de Poetry sayfasında yer alıyordu; bu yüzden 78 satır 73'e indi. Classics kitaplarından Alice in Wonderland'in sayfasında açıklama bloğu yok. Yedi kitap "Default" kategorisini taşıyordu. Fiyatlar 12,84 ile 58,63 arasında değiştiği için aykırı değer kuralı hiçbir şeyi işaretlemedi; bu site için doğru sonuç da bu. dtypes çıktısı metin için str, fiyat için float64, sayımlar için Int64, kategori için category ve zaman damgası için datetime64[us, UTC] gösteriyor.
Her adım ne yapar ve neden?
Metin ve Unicode
str.strip(), HTML düzeninin değerlerin çevresinde bıraktığı boşlukları ve satır sonlarını siler. "" değerini eksik değere çevirmek göründüğünden daha önemlidir: isna() boş dizeleri saymaz; bu adım olmadan boş bir alan dolu gibi geçer.
Kodlama onarımı yanlış tahmini geri alır. s.encode("latin-1").decode("utf-8"), Requests'in ürettiği karakterleri alır, onları özgün baytlara geri çevirir ve bu baytları UTF-8 olarak çözer. Zaten doğru olan bir dize ya değişmeden geçer ya da iki çağrıdan birinde hata verir ve olduğu gibi bırakılır. Daha temiz çözüm kaynaktadır: BeautifulSoup'a response.content verin ya da response.text okumadan önce response.encoding = "utf-8" ayarlayın. Onarım yine de önceden kaydedilmiş dosyalar için bilinmeye değer. Ardından unicodedata.normalize("NFKC", s), bitişik harfler (ligatür) ve bölünmez boşluklar gibi uyumluluk karakterlerini sade biçimlerine indirger (unicodedata belgeleri); böylece aynı başlığın iki farklı yazımı karşılaştırıldığında eşit çıkar.
Eksik değerler
Betik, eksik değerleri sütun bazında sayar ve bir has_description işareti ekler. Boşluğa "No description" yazmaz, çünkü bu dize ileride gerçek bir metin gibi görünür. Sayılarda boşlukları ortalama ya da medyanla doldurmak temizleme adımının değil, analistin bunu grafikte belirtebileceği analiz adımının işidir.
Sayılar, puanlar ve tarihler
pd.to_numeric(..., errors="coerce"), ayrıştıramadığı her şeyi durmak yerine NaN'a çevirir. Ardından bir doğrulama kontrolü bu NaN'ları saydığı sürece, bir temizleme turu için doğru varsayılan budur. Boş değer alabilen Int64 türü (büyük I ile) bir değer eksik olsa bile tam sayıları tam sayı olarak tutar; sıradan int64 NaN taşıyamaz ve sessizce float'a dönüşür. Tarihler utc=True ile ayrıştırılır; böylece farklı çalıştırmalardan ve makinelerden gelen zaman damgaları doğru karşılaştırılır.
Kategoriler ve yinelenen kayıtlar
astype("category") her etiketi bir kez saklar ve her satırda küçük bir kod tutar. Bellek tasarrufunun ötesinde etiket listesini de açık hâle getirir: df["category"].cat.categories, "Poetry" ile "poetry " yazımlarının ikisinin de ayakta kalıp kalmadığını bir bakışta gösterir.
Yinelenen kayıtlar için satırı değil, nesneyi tanımlayan bir anahtar seçin. Burada bu, her kitap sayfasında yazan UPC'dir. Bu sitede URL de işe yarardı, ama gerçek mağazalarda aynı ürünün çoğu zaman birden fazla URL'si olur (izleme parametreleri, kategori yolları). drop_duplicates(keep="last") öncesinde scraped_at'e göre sıralamak en yeni kopyayı tutar; fiyatlar çalıştırmalar arasında değişiyorsa istediğiniz de budur.
Aykırı değerler ve doğrulama
Çeyrekler açıklığı (IQR) kuralı, birinci çeyreğin altında ya da üçüncü çeyreğin üstünde IQR'nin 1,5 katından fazla uzaklaşan değerleri işaretler. Açıklaması kolaydır ve normal dağılım varsaymaz. Betik satırları silmek yerine bir price_outlier sütunu ekler: gerçek bir mağazada 0,99'luk bir fiyat gerçek bir indirim de olabilir, 10.99 içinden 0.99 okuyan bir ayrıştırma hatası da. Herhangi bir şey silinmeden önce bir insan bakmalıdır.
validate() son savunma hattıdır. Ham fiyatlardan birini N/A, puanlardan birini Six yaptığımızda çalıştırma, hatalı bir dosya yazmak yerine Validation failed: prices that did not parse, ratings missing or outside 1-5 mesajıyla durdu. Bu kontrolleri verinizin gerçek kurallarına yakın tutun; hiçbir zaman başarısız olamayacak bir kontrol hiçbir şeyi korumaz.
Temizlenmiş veri nerelerde kullanılır?
- Fiyat takibi: Fiyat geçmişi ve fiyat uyarısı için zaman damgalı sayısal fiyatlar gerekir. Fiyat takibi sayfasına ve rakip fiyat takibi yazısına bakın.
- Pazar araştırması: Bir katalog genelinde kategori payları, puan dağılımları ve stok seviyeleri. Pazar araştırması sayfasına bakın.
- Veri madenciliği ve modelleme: Modeller türü belli, tekilleştirilmiş girdiler ister. Konunun arka planını Veri Madenciliği Nedir? yazısı anlatıyor.
- Veri hatları: Bu betik, küçük bir ETL işinin "dönüştürme" adımıdır; kalıbın tamamı ETL Nedir? yazısında.
- Saklama: Sütunların türleri belli olduktan sonra CSV, JSON ve SQLite arasında nasıl seçim yapacağınızı Kazınan Veri CSV, JSON ve SQLite'a Nasıl Kaydedilir? yazısında anlattık.
- Büyük ölçekte toplama: Daha büyük kazıma projeleri ve çevrelerindeki altyapı veri kazıma sayfamızda.
Yaygın hatalar
- Tek kopyayı temizlemek. Ham dosyanın üzerine yazmak, her kural değişikliğinde yeniden kazımak zorunda kalmak demektir.
- Önce tekilleştirmek.
drop_duplicatesdeğerleri birebir karşılaştırır; boşlukları kırpmadan önce çalıştırırsanız"Olio"ile"Olio "ikisi de kalır. - Fiyat metinlerinde
astype(float). İlk£işaretinde hata verir. Sayı olmayan karakterleri silin, ardındanerrors="coerce"ilepd.to_numerickullanın ve ortaya çıkanNaN'ları sayın. - Boşlukları sıfırla doldurmak. Eksik bir stok adedi, sıfır stok demek değildir.
Int64kullanın ve değeri eksik bırakın. - Zincirleme atama. pandas 3'te
df[df.price > 50]["flag"] = Truehiçbir şeyi değiştirmez, yalnızca uyarı verir.df.loc[df.price > 50, "flag"] = Truekullanın. - Excel için BOM'suz kaydetmek. Windows'taki Excel düz bir UTF-8 CSV'yi eski bir kod sayfasıyla okur ve
£yine bozulur;encoding="utf-8-sig"bunu önler. - Daha iyi temizlemek yerine daha sert kazımak. Veri, istekler başarısız olduğu için eksikse toplama tarafını düzeltin: robots.txt kurallarına uyun, HTTP 429 aldığınızda yavaşlayın ve varsa resmî bir API'yi tercih edin.
Karar rehberi
| İhtiyaç | Öneri |
|---|---|
| Metin olarak saklanan sayılar | Simgeleri silin, pd.to_numeric(errors="coerce"), ardından NaN'ları sayın |
| Boşluk içeren tam sayılar | int64 ya da float değil, boş değer alabilen Int64 |
£, ’ veya é içeren metin | Kod çözmeyi kaynakta düzeltin; kaydedilmiş dosyaları latin-1 gidiş dönüşüyle onarın |
| Birkaç sayfadan gelen aynı ürün | Değişmeyen bir kimliğe göre drop_duplicates, en yeni kopya kalır |
| Birkaç tuhaf fiyat | IQR kuralıyla işaretleyip gözden geçirin; otomatik silmeyin |
| Kategori gibi tekrarlanan etiketler | astype("category") ve yer tutucular için açık bir eşleme |
| Farklı çalıştırmalardan gelen zaman damgaları | pd.to_datetime(..., utc=True) |
| Adımlar arasında türleri korumak | CSV'nin yanına Parquet de kaydedin |
| Büyük veya düzenli tekrarlanan kazımalar | Hız sınırları, yeniden denemeler ve hacim gerektiriyorsa pr.proxynet.io:8000 üzerinden Rotating Proxy |
Sıkça sorulan sorular
Veriyi kazırken mi temizlemeliyim, sonra mı?
Kazırken en azını yapın (metni alın, boşlukları kırpın), gerisini sonra pandas'ta halledin. Ham dosyayı saklamak, temizleme kurallarını değiştirip siteye tek bir yeni istek göndermeden saniyeler içinde yeniden çalıştırmanızı sağlar.
Fiyat sütunum neden £ yerine £ gösteriyor?
Sunucu bir karakter kümesi bildirmediği için Requests UTF-8 baytlarını ISO-8859-1 olarak çözdü. Ayrıştırıcınıza response.content verin ya da response.encoding = "utf-8" ayarlayın. Zaten kaydedilmiş veride s.encode("latin-1").decode("utf-8") hatayı geri alır.
Eksik değerler için doğru yol dropna() mı?
Yalnızca o alanı olmayan bir satır sorunuz için işe yaramıyorsa. Argümansız dropna(), herhangi bir sütunu eksik olan satırı atar; bu da tek bir isteğe bağlı alan yüzünden kazınmış bir veri setinin büyük kısmını silebilir. Gerçekten ihtiyaç duyduğunuz sütunlar için dropna(subset=[...]) kullanın, geri kalanları işaretleyin.
Yinelenen kayıtları ayıklamak için sütunu nasıl seçerim?
Sayfada yazan bir tanımlayıcı kullanın: UPC, SKU, ISBN ya da ürün kimliği gibi. URL'ler yalnızca her ürünün tam olarak bir URL'si olduğu sitelerde işe yarar. Kimlik yoksa normalleştirilmiş birkaç alanı birleştirin, örneğin başlık ile markayı.
Bu kod pandas 2 ile çalışır mı?
Yalnızca pandas 3.0.6 üzerinde test ettik; ancak kullandığı her çağrı pandas 2.x'te de var ve metin sütunu seçimi hem eski object türünü hem de yeni str türünü kapsıyor. Basılan çıktıda küçük farklar bekleyin: str yerine object, mikrosaniye yerine nanosaniye çözünürlüklü zaman damgaları gibi. İki sürüm arasında farklı davranan asıl şey, zincirleme atamaya dayanan koddur.
Veriyi temizlemek için proxy gerekir mi?
Hayır. Temizleme sizin makinenizde çalışır ve hiç trafik göndermez. Proxy yalnızca toplama aşamasında ve yalnızca tek bir IP adresinin bir sitenin hız sınırlarını aşacağı hacimlerde önem kazanır. Sayfalar hangi ağ üzerinden gelmiş olursa olsun temizleme kodu aynı kalır.
Özet
Kazınan veri, insan okurlar için biçimlendirilmiş metin olarak gelir. Güvenilir bir temizleme turu ham dosyayı saklar, önce metni ve kodlamayı düzeltir, her sütunu gerçek türüne dönüştürür, yinelenen kayıtları değişmeyen bir anahtara göre ayıklar, tuhaf değerleri silmek yerine işaretler ve kaydetmeden önce başarısız bir kontrolde durur. Yukarıdaki betik bunların hepsini, sayfalar indirildikten sonra 78 gerçek satır üzerinde bir saniyeden kısa sürede yapıyor. Toplama işiniz bir alıştırma sitesinin ötesine geçtiğinde ağ tarafını proxy sayfamızdaki paketler karşılar.




