Meskipun pendekatan pengumpulan data ini terlihat bagus, namun pendekatan ini tidak disukai oleh banyak situs web, dan ada konsekuensi yang harus ditanggung jika menindaklanjuti dengan scraping, seperti pelarangan IP kita.
Pada catatan positifnya, layanan proxy membantu menghindari konsekuensi ini. Layanan proxy memungkinkan kita untuk menggunakan IP yang berbeda ketika mengumpulkan data secara online, dan seaman apa pun kelihatannya, menggunakan banyak proxy lebih baik. Menggunakan banyak proxy saat melakukan scraping membuat interaksi dengan situs web tampak acak dan meningkatkan keamanan.
Situs web target (sumber) untuk panduan ini adalah toko buku online. Ini meniru situs web e-commerce untuk buku. Di dalamnya terdapat buku-buku dengan nama, harga, dan ketersediaan. Karena panduan ini tidak berfokus pada pengorganisasian data yang dikembalikan tetapi pada proxy yang berputar, data yang dikembalikan hanya akan ditampilkan di konsol.
Instal dan impor beberapa modul Python ke dalam berkas kita sebelum kita dapat mulai mengkodekan fungsi-fungsi yang akan membantu dalam merotasi proxy dan mengikis situs web.
pip install requests beautifulSoup4 lxml
3 dari 5 modul Python yang dibutuhkan untuk skrip scraping ini dapat diinstal menggunakan perintah di atas. Requests memungkinkan kita untuk mengirim permintaan HTTP ke situs web, beautifulSoup4 memungkinkan kita untuk mengekstrak informasi dari HTML halaman yang disediakan oleh permintaan, dan LXML adalah pengurai HTML.
Selain itu, kita juga membutuhkan modul threading bawaan untuk memungkinkan beberapa pengujian proksi untuk melihat apakah proksi tersebut berfungsi dan json untuk membaca dari file JSON.
import requests
import threading
from requests.auth import HTTPProxyAuth
import json
from bs4 import BeautifulSoup
import lxml
import time
url_to_scrape = "https://books.toscrape.com"
valid_proxies = []
book_names = []
book_price = []
book_availability = []
next_button_link = ""
Membuat skrip scraping yang merotasi proksi berarti kita membutuhkan daftar proksi untuk dipilih selama rotasi. Beberapa proksi memerlukan autentikasi, dan yang lainnya tidak. Kita harus membuat daftar kamus dengan detail proxy, termasuk nama pengguna dan kata sandi proxy jika otentikasi diperlukan.
Pendekatan terbaik untuk hal ini adalah dengan meletakkan informasi proksi kita dalam berkas JSON terpisah yang disusun seperti di bawah ini:
[
{
"proxy_address": "XX.X.XX.X:XX",
"proxy_username": "",
"proxy_password": ""
},
{
"proxy_address": "XX.X.XX.X:XX",
"proxy_username": "",
"proxy_password": ""
},
{
"proxy_address": "XX.X.XX.X:XX",
"proxy_username": "",
"proxy_password": ""
},
{
"proxy_address": "XX.X.XX.X:XX",
"proxy_username": "",
"proxy_password": ""
}
]
Pada bidang "proxy_address", masukkan alamat IP dan port, dipisahkan dengan titik dua. Pada kolom "proxy_username" dan "proxy_password", masukkan nama pengguna dan kata sandi untuk otorisasi.
Di atas adalah isi berkas JSON dengan 4 proksi yang dapat dipilih oleh skrip. Nama pengguna dan kata sandi dapat dikosongkan, menunjukkan proxy yang tidak memerlukan autentikasi.
def verify_proxies(proxy:dict):
try:
if proxy['proxy_username'] != "" and proxy['proxy_password'] != "":
proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
res = requests.get(
url_to_scrape,
auth = proxy_auth,
proxies={
"http" : proxy['proxy_address']
}
)
else:
res = requests.get(url_to_scrape, proxies={
"http" : proxy['proxy_address'],
})
if res.status_code == 200:
valid_proxies.append(proxy)
print(f"Proxy Validated: {proxy['proxy_address']}")
except:
print("Proxy Invalidated, Moving on")
Sebagai tindakan pencegahan, fungsi ini memastikan bahwa proksi yang disediakan aktif dan berfungsi. Kita dapat mencapai hal ini dengan melakukan perulangan melalui setiap kamus dalam berkas JSON, mengirimkan permintaan GET ke situs web, dan jika kode status 200 dikembalikan, tambahkan proksi tersebut ke dalam daftar valid_proksi - variabel yang telah kita buat sebelumnya untuk menyimpan proksi yang bekerja dari daftar dalam berkas. Jika panggilan tidak berhasil, eksekusi akan berlanjut.
Karena beautifulSoup membutuhkan kode HTML situs web untuk mengekstrak data yang kita butuhkan, kita telah membuat request_function(), yang mengambil URL dan proxy pilihan dan mengembalikan kode HTML sebagai teks. Variabel proxy memungkinkan kita untuk merutekan requestl melalui proxy yang berbeda, sehingga merotasi proxy.
def request_function(url, proxy):
try:
if proxy['proxy_username'] != "" and proxy['proxy_password'] != "":
proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
response = requests.get(
url,
auth = proxy_auth,
proxies={
"http" : proxy['proxy_address']
}
)
else:
response = requests.get(url, proxies={
"http" : proxy['proxy_address']
})
if response.status_code == 200:
return response.text
except Exception as err:
print(f"Switching Proxies, URL access was unsuccessful: {err}")
return None
data_extract() mengekstrak data yang kita butuhkan dari kode HTML yang disediakan. Fungsi ini mengumpulkan elemen HTML yang berisi informasi buku seperti nama buku, harga, dan ketersediaan. Ini juga mengekstrak tautan untuk halaman berikutnya.
Hal ini sangat rumit karena tautannya bersifat dinamis, jadi kami harus memperhitungkan kedinamisannya. Akhirnya, ia melihat seluruh buku dan mengekstrak nama, harga, dan ketersediaan, lalu mengembalikan tautan tombol berikutnya yang akan kita gunakan untuk mengambil kode HTML dari halaman berikutnya.
def data_extract(response):
soup = BeautifulSoup(response, "lxml")
books = soup.find_all("li", class_="col-xs-6 col-sm-4 col-md-3 col-lg-3")
next_button_link = soup.find("li", class_="next").find('a').get('href')
next_button_link=f"{url_to_scrape}/{next_button_link}" if "catalogue" in next_button_link else f"{url_to_scrape}/catalogue/{next_button_link}"
for each in books:
book_names.append(each.find("img").get("alt"))
book_price.append(each.find("p", class_="price_color").text)
book_availability.append(each.find("p", class_="instock availability").text.strip())
return next_button_link
Untuk menghubungkan semuanya, kita harus melakukannya:
with open("proxy-list.json") as json_file:
proxies = json.load(json_file)
for each in proxies:
threading.Thread(target=verify_proxies, args=(each, )).start()
time.sleep(4)
for i in range(len(valid_proxies)):
response = request_function(url_to_scrape, valid_proxies[i])
if response != None:
next_button_link = data_extract(response)
break
else:
continue
for proxy in valid_proxies:
print(f"Using Proxy: {proxy['proxy_address']}")
response = request_function(next_button_link, proxy)
if response is not None:
next_button_link = data_extract(response)
else:
continue
for each in range(len(book_names)):
print(f"No {each+1}: Book Name: {book_names[each]} Book Price: {book_price[each]} and Availability {book_availability[each]}")
import requests
import threading
from requests.auth import HTTPProxyAuth
import json
from bs4 import BeautifulSoup
import time
url_to_scrape = "https://books.toscrape.com"
valid_proxies = []
book_names = []
book_price = []
book_availability = []
next_button_link = ""
def verify_proxies(proxy: dict):
try:
if proxy['proxy_username'] != "" and proxy['proxy_password'] != "":
proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
res = requests.get(
url_to_scrape,
auth=proxy_auth,
proxies={
"http": proxy['proxy_address'],
}
)
else:
res = requests.get(url_to_scrape, proxies={
"http": proxy['proxy_address'],
})
if res.status_code == 200:
valid_proxies.append(proxy)
print(f"Proxy Validated: {proxy['proxy_address']}")
except:
print("Proxy Invalidated, Moving on")
# Mengambil elemen HTML dari sebuah halaman
def request_function(url, proxy):
try:
if proxy['proxy_username'] != "" and proxy['proxy_password'] != "":
proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
response = requests.get(
url,
auth=proxy_auth,
proxies={
"http": proxy['proxy_address'],
}
)
else:
response = requests.get(url, proxies={
"http": proxy['proxy_address'],
})
if response.status_code == 200:
return response.text
except Exception as err:
print(f"Switching Proxies, URL access was unsuccessful: {err}")
return None
# Mengikis
def data_extract(response):
soup = BeautifulSoup(response, "lxml")
books = soup.find_all("li", class_="col-xs-6 col-sm-4 col-md-3 col-lg-3")
next_button_link = soup.find("li", class_="next").find('a').get('href')
next_button_link = f"{url_to_scrape}/{next_button_link}" if "catalogue" in next_button_link else f"{url_to_scrape}/catalogue/{next_button_link}"
for each in books:
book_names.append(each.find("img").get("alt"))
book_price.append(each.find("p", class_="price_color").text)
book_availability.append(each.find("p", class_="instock availability").text.strip())
return next_button_link
# Dapatkan proxy dari JSON
with open("proxy-list.json") as json_file:
proxies = json.load(json_file)
for each in proxies:
threading.Thread(target=verify_proxies, args=(each,)).start()
time.sleep(4)
for i in range(len(valid_proxies)):
response = request_function(url_to_scrape, valid_proxies[i])
if response is not None:
next_button_link = data_extract(response)
break
else:
continue
for proxy in valid_proxies:
print(f"Using Proxy: {proxy['proxy_address']}")
response = request_function(next_button_link, proxy)
if response is not None:
next_button_link = data_extract(response)
else:
continue
for each in range(len(book_names)):
print(
f"No {each + 1}: Book Name: {book_names[each]} Book Price: {book_price[each]} and Availability {book_availability[each]}")
Setelah eksekusi berhasil, hasilnya terlihat seperti di bawah ini. Proses ini dilanjutkan dengan mengekstrak informasi lebih dari 100 buku menggunakan 2 proksi yang disediakan.
Menggunakan beberapa proxy untuk scraping web memungkinkan peningkatan jumlah permintaan ke sumber daya target dan membantu mem-bypass pemblokiran. Untuk menjaga stabilitas proses scraping, disarankan untuk menggunakan alamat IP yang menawarkan kecepatan tinggi dan faktor kepercayaan yang kuat, seperti ISP statis dan proxy residensial dinamis. Selain itu, fungsionalitas skrip yang disediakan dapat dengan mudah diperluas untuk mengakomodasi berbagai kebutuhan pengikisan data.
Komentar: 0