---
title: "Python Türkçe Karakter Sorunu: Encoding Nasıl Düzeltilir?"
description: "Python'da Türkçe karakter sorunu, baytların yanlış kodlamayla çözülmesinden çıkar. Dosyada, konsolda ve web sayfasında encoding'i düzeltmeyi anlatıyoruz."
url: https://proxynet.io/tr/blog/python-unicode-encoding-errors
date: 2026-09-24
author: "Acar Diveroli"
category: "Web Scraping, Nasıl Yapılır"
lang: tr
---

# Python Türkçe Karakter Sorunu: Encoding Nasıl Düzeltilir?

Bir betik, Türkiye'deki eski bir belediye sitesinden ihale ilanlarını topluyor. Tarayıcıda başlık `Çankırı İhale İlanı` diye görünüyor, betik ise `Çankýrý Ýhale Ýlaný` yazdırıyor. Betiğin bir Windows dizüstü bilgisayarda kaydettiği liste de Linux sunucuda açılırken `'utf-8' codec can't decode byte 0xc7 in position 0: invalid continuation byte` hatası veriyor. İki sorunun sebebi aynı: baytlar bir kod tablosuyla yazılmış, başka bir kod tablosuyla okunmuş.

Bu yazıda sorunu göründüğü her yerde düzeltiyoruz: kaynak dosyalar, `open()`, Windows konsolu, Requests ve Beautiful Soup ile çekilen sayfalar ve Excel için yazılan CSV dosyaları. Bozulmuş metni nasıl okuyup onaracağınızı da gösteriyoruz. Aşağıdaki her bayt değeri ve hata mesajı, Python 3.13 ile yaptığımız kendi denemelerimizden geliyor.

> **Not: Kısa cevap**
>
> Python 3'te `str` zaten Unicode'dur. Karakterler yalnızca baytlar metne çözülürken ya da metin bayta kodlanırken yanlış kodlama kullanıldığında bozulur. Her `open()` çağrısına `encoding="utf-8"` verin; `cp1254` gibi eski bir kod sayfasını yalnızca onunla yazılmış dosyalarda kullanın. Windows'ta yönlendirilmiş çıktı `'charmap'` hatası veriyorsa `PYTHONUTF8=1` ayarlayın. Requests, header'ında charset olmayan bir `text/html` yanıtını ISO-8859-1 sayar; bu yüzden Beautiful Soup'a `r.content` verin ya da `r.encoding` değerini kendiniz belirleyin. Excel için CSV dosyalarını `utf-8-sig` ile yazın.

## Python'da str ile bytes arasındaki fark nedir?

`str` karakterleri tutar ve her karakterin bir Unicode kod noktası vardır: Türkçedeki noktasız `ı` U+0131, `é` ise U+00E9'dur. Disk ve ağ yalnızca bayt saklar; metin dışarı çıkarken kodlanır (encode), içeri girerken çözülür (decode). Hangi baytın hangi karakteri temsil ettiğini kodlama (encoding) belirler, Python da bu eşleme tablolarına codec der. UTF-8'de `ı` iki bayttır: `C4 B1`. Eski Türkçe Windows kod sayfası windows-1254'te ise tek bayttır: `FD`. Batı Avrupa dilleri için kullanılan cp1252'de ve ISO-8859-1'de `ı` hiç yoktur.

Yazan ile okuyan farklı kodlama kullandığında baytlar sağlam kalır ama harfler değişir. Buna mojibake denir ve çoğu zaman geri alınabilir.

## Bir karakter nasıl başka bir karaktere dönüşür?

Her kodlama hatası aynı adımları izler:

1. **Metniniz Unicode'dur.** Python'da `"Çankırı"` yedi kod noktasıdır.
2. **A kodlamasıyla kodlanır.** Türkçe Windows bu metni cp1254 ile `C7 61 6E 6B FD 72 FD` olarak yazar.
3. **Yalnızca baytlar taşınır.** Bir dosya, bir HTTP gövdesi ya da bir pipe, A kodlamasını kendiliğinden bildirmez; bu bilgiyi ancak bir header, bir BOM ya da bir `<meta>` etiketi ekler.
4. **Biri B kodlamasıyla çözer.** ISO-8859-1 `FD` baytını `ý` harfine eşler ve ortaya `Çankýrý` çıkar. UTF-8 ise `C7 61` dizisini bir karakter olarak okuyamaz ve `UnicodeDecodeError` fırlatır.
5. **Çıktı yeniden kodlanır.** `print()` ve `write()` konsolun ya da dosyanın kodlamasını kullanır; o kodlamada bulunmayan bir karakter `UnicodeEncodeError` verir.

Hangi yönün bozulduğunu traceback söyler.

## Bozuk metin size ne söyler?

Bozulmanın biçimi, hangi iki kodlamanın karıştığını gösterir.

| Gördüğünüz | Gerçek metin | Ne oldu | Ne yapmalı |
|---|---|---|---|
| `cafÃ©`, `Ã¼`, `Ä±`, `ÅŸ` | `café`, `ü`, `ı`, `ş` | UTF-8 baytları (`C3 A9`, `C4 B1`) cp1252 ya da ISO-8859-1 ile çözülmüş | Baytları UTF-8 olarak çözün ya da metni onarın (aşağıda) |
| `ý`, `þ`, `ð`, `Ý` | `ı`, `ş`, `ğ`, `İ` | windows-1254 baytları (`FD`, `FE`, `F0`, `DD`), Requests'in varsayılanı olan ISO-8859-1 ile çözülmüş | `r.encoding = "cp1254"` ayarlayın ya da Beautiful Soup'a `r.content` verin |
| `ş` yerine `ţ` | `ş` | Tahmin, Türkçe bir sayfa için windows-1250'yi seçmiş | O sitenin kodlamasını sabitleyin |
| `�` | ASCII dışındaki herhangi bir harf | Baytlar `errors="replace"` ile UTF-8 olarak çözülmüş | Metinde kayboldu; ham baytları yeniden çözün |
| `?` | Herhangi bir harf | Metin, o harfi içermeyen bir kodlamaya `errors="replace"` ile kodlanmış | Kayboldu; UTF-8 ile yazın |
| Boş bir kutu | Doğru harf | Fontta o harfin glifi yok (PDF, eski terminal) | Kodlamayı değil fontu değiştirin |

## Python 3'te `# -*- coding: utf-8 -*-` satırı hâlâ gerekli mi?

Hayır. [PEP 3120](https://peps.python.org/pep-3120/) Python 3.0 ile kaynak dosyaların varsayılan kodlamasını UTF-8 yaptı; bu satır Python 2'den kalmadır. `.py` dosyanızdaki metin sabitleri hâlâ bozuluyorsa editörünüz dosyayı eski bir kod sayfasıyla (Windows'ta "ANSI") kaydetmiştir; dosyayı UTF-8 olarak kaydedin. `u""` öneki ve `codecs.open()` gibi diğer Python 2 öğütlerine de gerek yok.

## Dosya okurken ve yazarken encoding nasıl belirtilir?

`encoding` değerini her seferinde verin: `open("cities.txt", "w", encoding="utf-8")`. Verilmezse Python 3.13 Windows'ta ANSI kod sayfasını kullanır. Türkçe Windows makinemizde `locale.getpreferredencoding(False)` `cp1254` döndürdü ve düz bir `open("cities.txt", "w")` çağrısı `Çankırı` sözcüğünü `C7 61 6E 6B FD 72 FD` olarak yazdı. İngilizce Windows ise içinde `ı` olmayan cp1252'yi kullanır; aynı yazma işlemi orada `'charmap' codec can't encode character 'ı'` hatasıyla durur.

Size gelen dosyalarda, dosyanın yazıldığı kodlamayı kullanın: eski Türkçe Windows ve Excel çıktıları için `cp1254`, Batı Avrupa dillerindekiler için `cp1252`, Arapça ve Farsça için `cp1256`. Sığmayan baytlara ne olacağına `errors` argümanı karar verir:

- Varsayılan `strict` hata fırlatır. Doğru kodlamayı ararken istediğiniz de budur.
- `replace` her bozuk baytın yerine `�` koyar, böylece hasarın nerede olduğunu görürsünüz.
- `backslashreplace` bozuk baytları `\xfd` biçiminde görünür bırakır.
- `ignore` onları sessizce siler: cp1254 ile yazılıp UTF-8 olarak okunan `Iğdır`, `Idr` oldu.

"Latin-1 ile deneyin" öğüdü de aynı sorunu doğurur. ISO-8859-1, 256 bayt değerinin hepsini bir karaktere eşlediği için hiç hata vermez ve Türkçe metin sessizce `Çankýrý` olur.

### UnicodeDecodeError: 'utf-8' codec can't decode byte

Dosya UTF-8 değildir ve mesajdaki bayt bir ipucudur. Türkçe veride `0xfd`, `0xfe` ya da `0xf0` cp1254'ü gösterir. `0xe9` (é) Batı Avrupa verisinde cp1252'ye işaret eder; `0xfc` (ü) ise iki kod sayfasında da aynı harf olduğu için tek başına ikisini ayırmaz. pandas'ta kodlamayı parametre olarak verin: `pd.read_csv("export.csv", sep=";", encoding="cp1254")`.

## Windows'ta UnicodeEncodeError: 'charmap' codec can't encode character

Etkileşimli Windows konsolu Python 3.6'dan beri (PEP 528) UTF-8 yazar, ama bir dosyaya ya da pipe'a yönlendirilen çıktı ANSI kod sayfasını kullanır. Türkçe harfler cp1254'e sığar, ok işareti sığmaz: `print("Istanbul → Ankara")` içeren betik `python script.py > out.txt` ile çalıştırıldığında `'charmap' codec can't encode character '→'` hatası verdi. Üç çözüm işe yaradı:

- `set PYTHONUTF8=1` (PowerShell'de `$env:PYTHONUTF8=1`) dosyalar ve standart akışlar için UTF-8 modunu açar.
- `python -X utf8 script.py` aynısını tek bir çalıştırma için yapar.
- `PYTHONIOENCODING=utf-8` yalnızca standart akışları değiştirir, `open()` çağrısını değil.

UTF-8 modu, Python'un [Windows rehberinde](https://docs.python.org/3/using/windows.html#utf-8-mode) anlatılıyor. [PEP 686](https://peps.python.org/pep-0686/) UTF-8 modunu Python 3.15'ten itibaren varsayılan yapıyor; 3.15'in kararlı sürümü 1 Ekim 2026 için planlanmış durumda. Her makine bu sürümü çalıştırana kadar kodunuzda `encoding="utf-8"` kalsın.

`'latin-1' codec can't encode character` biçimindeki hata genellikle bir HTTP header'ından gelir. Requests'in altta kullandığı `http.client`, header değerlerini Latin-1 ile kodlar; `Iğdır` değerini taşıyan bir header bu yüzden aynı hatayı verdi. Böyle değerleri yüzde kodlamasıyla (percent-encoding) gönderin ya da isteğin gövdesine koyun.

## Bir web sayfasının kodlaması nereden gelir?

Tarayıcı bir sayfanın kodlamasına şu sırayla karar verir:

1. Gövdenin başındaki bayt sırası işareti (BOM, byte order mark).
2. `Content-Type` header'ının `charset` parametresi.
3. [MDN'ye göre](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta) ilk 1024 bayt içinde yer alması gereken `<meta charset>` etiketi.
4. Baytlara bakılarak yapılan bir tahmin.

Requests yalnızca header'ı okur. Charset belirtilmemiş bir `text/*` türü için [Requests dokümantasyonu](https://requests.readthedocs.io/en/latest/user/advanced/#encodings) RFC 2616'ya uyduğunu ve ISO-8859-1 kullandığını söylüyor; oysa [RFC 7231](https://www.rfc-editor.org/rfc/rfc7231.html#appendix-B) bu varsayılanı 2014'te kaldırdı. Charset'siz bir JSON yanıtı UTF-8 olarak okunur, diğer türler için tahmin yapılır. Düz `text/html` olarak sunulan test sayfalarımızda `r.encoding` her seferinde `ISO-8859-1` çıktı. Bir sayfanın tarayıcıda doğru, betiğinizde yanlış görünmesinin sebebi bu.

### windows-1254 ile ISO-8859-9 aynı mı?

Neredeyse. İkisi de Türkçe harfleri `0xA0` ile `0xFF` arasında aynı baytlara koyar. `0x80` ile `0x9F` arasında ise windows-1254'te `€`, `“`, `”` ve `…` bulunur, ISO-8859-9'da görünmeyen kontrol kodları vardır. [WHATWG Encoding Standard](https://encoding.spec.whatwg.org/) tarayıcılara `iso-8859-9` etiketini windows-1254, `iso-8859-1` etiketini windows-1252 olarak okumalarını söyler; Python ise bunları ayrı codec'ler olarak ele alır. `iso-8859-9` etiketli sayfamızda Beautiful Soup `\x93Kampanya\x94 10\x80` üretti, `from_encoding="cp1254"` ile aynı baytlar `“Kampanya” 10€` oldu. Böyle sayfaları cp1254 ya da cp1252 ile çözün.

### Kendi HTML sayfanız charset'i nasıl bildirmeli?

Dosyayı UTF-8 olarak kaydedin, `<head>` bölümünün en başına `<meta charset="utf-8">` koyun ve sunucunun `Content-Type` header'ının başka bir charset belirtmediğinden emin olun, çünkü header önceliklidir.

## response.encoding, apparent_encoding mi, Beautiful Soup mu?

Her seçenek farklı bir sinyali okur:

- `r.encoding` header'dan gelir. `r.encoding = "cp1254"` ayarlamak bilinen tek bir siteyi düzeltir; her site için `"utf-8"` ayarlamak ise windows-1254 kullanan siteleri bozar (test sayfamızda `�ank�r�`).
- `r.apparent_encoding`, charset-normalizer'ın gövdeye bakarak yaptığı tahmindir. Türkçe sayfalarımızdan biri için `Windows-1254`, diğer ikisi için `windows-1250` dedi; bu da `ş` harfini `ţ` yapar.
- `BeautifulSoup(r.content, "html.parser")` `<meta>` etiketini kendisi okur. `r.text` değil, bayt verin; ayrıştırma tarafını [BeautifulSoup Nedir ve Python'da Nasıl Kullanılır?](/tr/blog/beautifulsoup-tutorial) yazımız anlatıyor.

Bizim sıramız tarayıcınınkini izler: önce BOM, sonra header'daki charset, ardından `<meta>` etiketi, en son tahmin. Hangisini kullandığınızı da log'a yazın. HTTPX farklı davranır: 0.28.1 sürümü header'da charset yoksa UTF-8 varsayar ve cp1254 sayfamız `�ank�r�` olarak geldi ([HTTPX, Requests ve AIOHTTP Karşılaştırması](/tr/blog/httpx-vs-requests-vs-aiohttp)).

## Sayfaları tarayıcı gibi çözen bir Python scraper

Bu scraper (web sayfalarından veri toplayan betik) her URL'yi bir kez çeker, kodlamayı yukarıdaki sırayla seçer, ISO etiketlerini tarayıcıların yaptığı gibi eşler ve başlıkları Excel için bir CSV'ye yazar. Çalışması için `pip install requests beautifulsoup4` yeterli. Yeniden deneme ya da IP döndürme yapmaz; bunlar için [Scraping'de HTTP Hata Kodları](/tr/blog/http-status-codes-web-scraping) ve [Python'da Proxy'ler Nasıl Döndürülür?](/tr/blog/rotating-proxy-python) yazılarımıza bakın.

```python
"""Sayfaları çeker, her birini bir tarayıcının yapacağı gibi çözer ve başlıkları Excel için bir CSV'ye kaydeder."""
import csv
import logging
import sys
from email.message import Message

import requests
from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector

PROXY = None  # örneğin "http://user:pass@pr.proxynet.io:8000"
USER_AGENT = "heading-reader/1.0 (+https://example.com/bot)"

# Tarayıcılar bu etiketleri Windows kod sayfaları olarak okur (WHATWG Encoding Standard); Python okumaz.
BROWSER_ALIASES = {
    "iso-8859-1": "cp1252", "iso8859-1": "cp1252", "latin1": "cp1252", "latin-1": "cp1252",
    "us-ascii": "cp1252", "ascii": "cp1252",
    "iso-8859-9": "cp1254", "iso8859-9": "cp1254", "latin5": "cp1254",
}

log = logging.getLogger("headings")

def header_charset(resp):
    """Content-Type header'ındaki charset ya da None. Requests'in kendi r.encoding
    değeri, charset'siz her text/* türü için burada ISO-8859-1 derdi."""
    msg = Message()
    msg["content-type"] = resp.headers.get("Content-Type", "")
    return msg.get_param("charset")

def pick_codec(resp):
    """(codec, geldiği yer) döndürür: önce BOM, sonra header, <meta charset>, en son tahmin."""
    bom = EncodingDetector.strip_byte_order_mark(resp.content)[1]
    if bom:
        return bom, "bom"
    declared = header_charset(resp)
    source = "header"
    if not declared:
        declared = EncodingDetector.find_declared_encoding(resp.content, is_html=True)
        source = "meta"
    if not declared:
        return resp.apparent_encoding or "utf-8", "guess"
    return BROWSER_ALIASES.get(declared.lower(), declared), source

def clean(text):
    """Bölünmez boşluk U+00A0 dahil, art arda gelen boşlukları teke indirir."""
    return " ".join(text.split())

def read_headings(session, url):
    resp = session.get(url, timeout=20)
    resp.raise_for_status()
    codec, source = pick_codec(resp)
    soup = BeautifulSoup(resp.content, "html.parser", from_encoding=codec)
    level = logging.WARNING if source == "guess" else logging.INFO
    log.log(level, "%s: %s from %s", url, codec, source)
    if soup.contains_replacement_characters:
        log.warning("%s: some bytes did not fit %s and became U+FFFD", url, codec)
    return [(url, codec, source, clean(h.get_text())) for h in soup.select("h1, h2")]

def main(urls, out="headings.csv"):
    session = requests.Session()
    session.headers["User-Agent"] = USER_AGENT
    if PROXY:
        session.proxies = {"http": PROXY, "https": PROXY}
    rows = []
    for url in urls:
        try:
            rows += read_headings(session, url)
        except requests.RequestException as exc:
            log.error("%s: %s", url, exc)
    # utf-8-sig önce bir BOM yazar, böylece Excel dosyayı UTF-8 olarak tanır
    with open(out, "w", newline="", encoding="utf-8-sig") as f:
        writer = csv.writer(f)
        writer.writerow(["url", "codec", "source", "heading"])
        writer.writerows(rows)
    log.info("%d headings written to %s", len(rows), out)

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(levelname)-7s %(message)s")
    main(sys.argv[1:] or ["https://example.com/"])
```

Betiği Python 3.13, Requests 2.34.2 ve Beautiful Soup 4.15.0 ile yerel bir HTTP proxy üzerinden çalıştırdık. Yerel test sayfaları, `utf8hdr` dışında, charset'siz `text/html` olarak sunuldu; son URL herkese açık bir sayfa:

```text
INFO    http://127.0.0.1:8057/cp1254: windows-1254 from meta
INFO    http://127.0.0.1:8057/iso9: cp1254 from meta
INFO    http://127.0.0.1:8057/utf8meta: utf-8 from meta
INFO    http://127.0.0.1:8057/utf8hdr: utf-8 from header
INFO    http://127.0.0.1:8057/latin1: cp1252 from meta
WARNING http://127.0.0.1:8057/nometa: windows-1250 from guess
WARNING https://example.com/: ascii from guess
INFO    12 headings written to headings.csv
```

Header'da charset ya da `<meta>` etiketi olan her sayfa doğru çıktı; ISO-8859-1 etiketli sayfadaki `“quoted” 5€` da buna dahil. İkisi de olmayan sayfa zayıf nokta: tahmin windows-1250 dedi ve CSV'ye `Çankýrý` ile `ţubat` yazıldı.

Aynı ülkedeki çok sayıda siteyi düzenli aralıklarla okuyorsanız, o ülkeden bir [Residential Proxy](https://proxynet.io/tr/residential-proxy) yerel ziyaretçilerin gördüğü sayfaları getirir; kodlama mantığı aynı kalır.

## "can't decode byte 0x8b" ve "0xa0" hataları ne anlama gelir?

### UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1

Bir gzip akışı `1F 8B` ile başlar ([RFC 1952](https://www.rfc-editor.org/rfc/rfc1952.html)); bu yüzden 1. konumdaki `0x8b`, sıkıştırılmış verinin metin olarak çözüldüğünü gösterir. `gzip.compress(...).decode("utf-8")` bize tam olarak bu mesajı verdi. Scraping'de bu hata, `r.raw` verisini doğrudan okuduğunuzda ya da sıkıştırılmış yanıtı hiçbir zaman açmayan `urllib.request` ile kopyalanmış bir `Accept-Encoding` header'ı gönderdiğinizde çıkar. Requests gzip'i kendisi açar; kopyalanmış header'ı kaldırın ve `r.content` kullanın. urllib3'te isteğe bağlı Brotli ya da Zstandard paketi yüklü değilse kopyalanmış `br` ya da `zstd` değerleri de aynı sonuca götürür.

### UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa0

cp1252, cp1254 ve ISO-8859-1'de bölünmez boşluk (U+00A0) tek bir `A0` baytıdır; UTF-8'de ise hiçbir karakter bu baytla başlamaz. Dosyayı gerçek kodlamasıyla okuyun. Karakter zaten metninizdeyse `"10\xa0kg".split()` onu kaldırır, `.split(" ")` kaldırmaz; `unicodedata.normalize("NFKC", s)` ise onu düz bir boşluğa çevirir (NFKC, `²` gibi karakterleri de `2` yapar).

## Bozulmuş metin nasıl onarılır?

Hiçbir bayt kaybolmadıysa yanlış adımı tersine çevirin:

- Ekrandan ya da bir hesap tablosundan kopyalanmış `Ä±ÅŸÄŸ` gibi bir metin: `s.encode("cp1252").decode("utf-8")` `ışğ` verir.
- Aynı bozulma Requests'in `r.text` çıktısından geliyorsa içinde cp1252'nin kodlayamadığı bir kontrol karakteri (`\x9f`) saklıdır; bu durumda `s.encode("latin-1").decode("utf-8")` kullanın. cp1252 bu karakterde `'charmap' codec can't encode character '\x9f'` hatasıyla durdu.
- ISO-8859-1 olarak okunmuş windows-1254 metni, örneğin `Çankýrý`: `s.encode("latin-1").decode("cp1254")` `Çankırı` verir.

Büyük ya da karışık veride [ftfy](https://pypi.org/project/ftfy/) 6.3.1 UTF-8 kaynaklı mojibake'yi onarır: `ftfy.fix_text("Ä±ÅŸÄŸ")` `ışğ` döndürdü. `Çankýrý` metnine ise dokunmadı, çünkü bu geçerli bir Latin metni gibi görünür; kod sayfası karışıklıklarını elle düzeltin. Harflerin yerinde `?` ya da `�` olan metin onarılamaz; kaynağı yeniden çekin.

## UTF-8 CSV dosyası Excel'de neden bozuk karakterlerle açılır?

Excel, çift tıklamayla açılan bir UTF-8 CSV'yi, dosya bir bayt sırası işaretiyle (BOM) başlıyorsa doğru gösterir; [Microsoft'un destek sayfası](https://support.microsoft.com/tr-tr/office/opening-csv-utf-8-files-correctly-in-excel-8a935af5-3416-4edd-ba7e-3dfd2bc4a032) da bunu belirtiyor. `encoding="utf-8-sig"` başa üç baytlık `EF BB BF` işaretini ekler; pandas'ta karşılığı `df.to_csv("out.csv", encoding="utf-8-sig")`. Bu dosyaları geri okurken de `utf-8-sig` kullanın: düz `utf-8` ile `csv` modülü bize ilk sütun adı olarak `﻿şehir` verdi, pandas ise BOM'u iki durumda da kaldırdı. Siteden Excel'e uzanan akışın tamamı [Web Sitesinden Veri Çekme](/tr/blog/extract-data-from-website) yazımızda.

## `"I".lower()` neden `"ı"` döndürmez?

`str.lower()` Unicode'un dilden bağımsız varsayılan eşlemesini kullanır. Türkçe için bu iki yerde yanlış sonuç verir: `"ISPARTA".lower()` `ısparta` yerine `isparta` döndürür, `"İ".lower()` ise `i` harfiyle birlikte bir birleşik nokta (U+0307) döndürür. `casefold()` da aynı eksikliği taşır. Önce iki özel harfi eşleyin:

```python
TR_LOWER = str.maketrans({"I": "ı", "İ": "i"})
TR_UPPER = str.maketrans({"i": "İ", "ı": "I"})

print("ISPARTA İZMİR".translate(TR_LOWER).lower())  # ısparta izmir
print("istanbul ışık".translate(TR_UPPER).upper())  # İSTANBUL IŞIK
```

Sayılar ve tarihler de yerel kurallara uyar. `1.299,90` gibi bir Türkçe fiyat, `s.replace(".", "").replace(",", ".")` ile `float()` fonksiyonunun okuyabileceği hâle gelir; pandas'ta da bunun için `decimal=","` ve `thousands="."` parametreleri var. Scraper'da `locale.setlocale()` kullanmaktan kaçının, çünkü bu çağrı bütün süreci etkiler. Fiyat temizlemenin tamamı [E-Ticarette Rakip Fiyat Takibi Nasıl Yapılır?](/tr/blog/competitor-price-tracking) yazımızda.

## Kullanım alanları

- **Fiyat takibi:** hâlâ eski kod sayfalarıyla sayfa sunan mağazalar ([rakip fiyat takibi](/tr/blog/competitor-price-tracking)).
- **Tabloları Excel'e aktarma:** doğru harflerle açılan bir CSV ([web sitesinden veri çekme](/tr/blog/extract-data-from-website)).
- **PHP scraper'ları:** `DOMDocument`'ın kendine özgü bir çözümü var ([PHP ile siteden veri çekme](/tr/blog/php-web-scraping)).
- **Scrapy dışa aktarımları:** çıktı kodlamasını `FEED_EXPORT_ENCODING` belirler ([Scrapy ile proxy kullanımı](/tr/blog/scrapy-proxy)).
- **.NET işleri:** `HttpClient` charset'i kendi yöntemiyle ele alır ([C# ile web sitesinden veri çekme](/tr/blog/csharp-web-scraping)).
- **Çok sayıda sitede tarama:** her sayfanın kodlamasını log'a yazın ([web crawler](/tr/web-crawler)).

## Sık yapılan hatalar

- **Hatayı `errors="ignore"` ya da latin-1 ile susturmak.** Betik çalışır, harfler kaybolur ya da değişir.
- **Her site için `r.encoding = "utf-8"` yazmak.** Bazı sayfaları düzeltir, windows-1254 kullananları bozar.
- **İki kez çözmek.** Zaten doğru çözülmüş metne `.encode().decode()` uygulamak, sağlam metne yeni bir hata ekler.
- **`iso-8859-9` ya da `iso-8859-1` etiketini olduğu gibi kullanmak.** Tarayıcılar windows-1254 ve windows-1252 kullanır; farkı tırnak işaretleri ve euro simgesi ortaya çıkarır.
- **`utf-8-sig` dosyasını `utf-8` olarak okumak.** İlk sütun adı görünmez bir karakterle başlar ve o sütunu adıyla arayan kod başarısız olur.
- **Karakter kodlamasını URL kodlamasıyla karıştırmak.** Proxy şifresindeki `%40` yüzde kodlamasıdır ve ayrı bir konudur ([Node.js'te proxy şifresindeki özel karakterler](/tr/blog/nodejs-proxy)).

## Karar rehberi

| İhtiyaç | Öneri |
|---|---|
| Kendi `.py` dosyanızda ASCII dışı metin | UTF-8 olarak kaydedin; coding satırına gerek yok |
| Dosya okuma ya da yazma | `encoding="utf-8"`; eski kod sayfası yalnızca onunla yazılmış dosyalar için |
| Windows'ta yönlendirilmiş çıktıda `'charmap'` hatası | `PYTHONUTF8=1` ya da `python -X utf8`; Python 3.15'ten itibaren varsayılan |
| Header'ında charset olmayan HTML | Beautiful Soup'a `r.content` verin, kodlamayı log'a yazın |
| Header'da charset yok, `<meta>` etiketi de yok | Sitenin kodlamasını sabitleyin; `apparent_encoding` son çare |
| Sayfa `iso-8859-9` ya da `iso-8859-1` diyor | Tarayıcı gibi cp1254 ya da cp1252 ile çözün |
| Metin zaten `Ã©` ya da `Ä±` gibi görünüyor | `.encode("cp1252").decode("utf-8")` ya da `ftfy.fix_text` |
| Sonuç Excel'de açılacak | CSV'yi `utf-8-sig` ile yazın |

## Sıkça sorulan sorular

### Python'da Türkçe karakterler için bir kütüphane gerekir mi?

Hayır. Python 3'te `str` Unicode'dur ve standart kütüphane yaygın kod sayfalarının codec'leriyle birlikte gelir ([standart kodlamalar](https://docs.python.org/3/library/codecs.html#standard-encodings)). Requests ile birlikte kurulan charset-normalizer bilinmeyen kodlamaları tahmin eder, ftfy ise mojibake'yi onarır.

### cp1252 ya da cp1254 bir dosya UTF-8'e nasıl çevrilir?

Dosyayı eski kodlamasıyla okuyun, yenisiyle yazın: `Path("new.txt").write_text(Path("old.txt").read_text(encoding="cp1254"), encoding="utf-8")`. `Path` sınıfı `pathlib` modülünden gelir. Orijinali silmeden önce birkaç ismi kontrol edin.

### errors="ignore" UnicodeDecodeError'ı çözer mi?

Hatayı gizler. Sığmayan baytlar uyarı verilmeden silinir ve isimler harf kaybeder. Bunun yerine doğru kodlamayı bulun.

### Bir dosyanın hangi kodlamayı kullandığını nasıl anlarım?

BOM ile başlamıyorsa bir metin dosyası kendi kodlamasını kaydetmez, bu yüzden her araç tahmin yürütür. `charset_normalizer.from_path("old.txt").best().encoding` Türkçe test dosyamız için `cp1254` döndürdü. Tahmini doğrulamak için dosyayı bu kodlamayla çözün ve Türkçe harf içeren birkaç sözcüğü okuyun.

### utf-8 ile utf-8-sig arasındaki fark nedir?

`utf-8-sig` yazarken başa bir BOM (`EF BB BF`) ekler, okurken onu atlar. Excel'de açılacak CSV dosyaları için bunu kullanın.

### pandas read_csv'de UnicodeDecodeError nasıl giderilir?

Dosyanın kodlamasını verin: Türkçe Windows çıktısı için `encoding="cp1254"`, Batı Avrupa çıktısı için `cp1252`, dosyada BOM varsa `utf-8-sig`. `encoding_errors="replace"` hatayı durdurur ama harfleri kaybettirir.

## Özet

Python'da Türkçe karakter sorunu tek bir kurala dayanır: baytları yazıldıkları kodlamayla çözün, kendi çıktınızı UTF-8 ile yazın. Hangi iki kodlamanın karıştığını bozuk metinden okuyun, her `open()` çağrısında `encoding` belirtin, Python 3.15 bunu sizin yerinize yapana kadar Windows'ta UTF-8 modunu kullanın ve web sayfalarını Beautiful Soup'a bayt olarak verin. Pek çok yerel siteden temiz metin toplaması gereken scraping işleri için [veri kazıma](/tr/data-scraping) çözümümüze göz atın.
