در این آموزش یاد می گیرید که چگونه تمام صفحات را از یک وب سایت خاص از Commoncrawl با پایتون استخراج کنید.
کد کامل را در حساب Github من پیدا خواهید کرد.
Commoncrawl چیست؟
Commoncrawl یک مخزن باز از داده های خزیدن وب است. این به شما امکان می دهد HTML استخراج شده را از تاریخچه خزیدن برای وب سایت ها یا صفحات وب خاص مشاهده کنید. این پایگاه داده ای است که معمولاً هنگام آموزش مدل های زبان بزرگ (LLM) مانند ChatGPT استفاده می شود.
چرا از Commoncrawl استفاده کنیم؟
از آنجایی که Commoncrawl حاوی داده های تاریخی در مورد تعداد زیادی از صفحات وب است، اغلب برای جایگزینی نیاز به مدیریت خراش وب استفاده می شود. پایگاه داده Commoncrawl را می توان برای موارد زیر استفاده کرد:
- تحلیل رقابتی،
- مشاهده تاریخچه برای یک صفحه وب،
- آموزش مدل های یادگیری ماشینی
در مورد ما، ما از آن برای استخراج صفحات HTML یک وب سایت به جای اینکه خودمان آن را خراش دهیم، با تمام مشکلاتی که ایجاد می کند (مانند مسدود شدن، پرداخت هزینه برای پراکسی ها و غیره) استفاده خواهیم کرد.
استفاده از Commoncrawl قبل از خراش دادن یک وبسایت، تمرین خوبی است، زیرا میتواند هزینه مالی اسکراپر و وبسایت خراششده و همچنین هزینه محیط اجرای یک خزنده وب برای محتوایی که قبلاً در جایی ذخیره شده است را کاهش دهد.
کتابخانه های مورد نیاز را نصب کنید
ابتدا باید کتابخانه های زیر (درخواست ها، urllib3، warcio و pandas) را نصب کنید. در ترمینال، این دستور را تایپ کنید تا تمام کتابخانه ها نصب شوند (هر کدام را که قبلاً نصب کرده اید حذف کنید):
pip3 install requests urllib3 warcio pandas beautifulsoup4
واردات کتابخانه ها
import requests
import json
import os
import pandas as pd
# For parsing URLs:
from urllib.parse import quote_plus
متغیرها و توابع پایتون خود را تنظیم کنید
متغیرهایی که باید تنظیم کنید عبارتند از target_url
و indexes
که می خواهید داده ها را از آن دریافت کنید. نمایهها مربوط به زمانی است که خزیده شده است و فهرست کامل را میتوان در اسناد رسمی یا از این فایل متنی (در هنگام اولین انتشار این پست استخراج شده) یافت.
def search_cc_index(url, index_name):
"""
Search the Common Crawl Index for a given URL.
This function queries the Common Crawl Index API to find records related to the specified URL.
It uses the index specified by `index_name` to retrieve the data and returns a list of JSON objects,
each representing a record from the index.
Arguments:
url (str): The URL to search for in the Common Crawl Index.
index_name (str): The name of the Common Crawl Index to search (e.g., "CC-MAIN-2024-10").
Returns:
list: A list of JSON objects representing records found in the Common Crawl Index.
Returns None if the request fails or no records are found.
Example:
>>> search_cc_index("example.com", "CC-MAIN-2024-10")
[{...}, {...}, ...]
"""
encoded_url = quote_plus(url)
index_url = f'http://index.commoncrawl.org/{index_name}-index?url={encoded_url}&output=json'
response = requests.get(index_url)
if response.status_code == 200:
records = response.text.strip().split('\n')
return [json.loads(record) for record in records]
else:
return None
def fetch_single_record(warc_record_filename, offset, length):
"""
Fetch a single WARC record from Common Crawl.
Arguments:
record {dict} -- A dictionary containing the WARC record details.
Returns:
bytes or None -- The raw content of the response if found, otherwise None.
"""
s3_url = f'https://data.commoncrawl.org/{warc_record_filename}'
# Define the byte range for the request
byte_range = f'bytes={offset}-{offset + length - 1}'
# Send the HTTP GET request to the S3 URL with the specified byte range
response = requests.get(
s3_url,
headers={'Range': byte_range},
stream=True
)
if response.status_code == 206:
# Use `stream=True` in the call to `requests.get()` to get a raw byte stream,
# because it's gzip compressed data
stream = ArchiveIterator(response.raw)
for warc_record in stream:
if warc_record.rec_type == 'response':
return warc_record.content_stream().read()
else:
print(f"Failed to fetch data: {response.status_code}")
return None
def append_df_row_to_pickle(row, pickle_file):
"""
Append a row to a DataFrame stored in a pickle file.
Arguments:
row {pd.Series} -- The row to be appended to the DataFrame.
pickle_file {str} -- The path to the pickle file where the DataFrame is stored.
"""
# Check if the pickle file exists
if os.path.exists(pickle_file):
# Load the existing DataFrame from the pickle file
df = pd.read_pickle(pickle_file)
else:
# If the file doesn't exist, create a new DataFrame
df = pd.DataFrame(columns=row.index)
# Append the new row to the DataFrame
df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
# Save the updated DataFrame back to the pickle file
df.to_pickle(pickle_file)
def load_processed_indices(pickle_file):
"""
Load processed indices from a pickle file to check previously processed records.
Arguments:
pickle_file {str} -- The path to the pickle file where the DataFrame is stored.
Returns:
Set of processed indices.
"""
if os.path.exists(pickle_file):
df = pd.read_pickle(pickle_file)
# Assuming 'index' column is in the DataFrame and contains indices of processed records
processed_indices = set(df['index'].unique())
print(f"Loaded {len(processed_indices)} processed indices from {pickle_file}")
return processed_indices
else:
print(f"No processed indices found. Pickle file '{pickle_file}' does not exist.")
return set()
همه صفحات را از فهرست Commoncrawl واکشی کنید
اولین گام در واکشی HTML از Commoncrawl این است که در فهرست جستجو کنید تا اطلاعات مورد نظر خود را در کجا ذخیره کنید. ما این کار را با استفاده از search_cc_index(target_url, index_name)
تابع
record_dfs = []
# Fetch each index and store into a datafram
for index_name in indexes:
print('Running: ', index_name)
records = search_cc_index(target_url,index_name)
record_df = pd.DataFrame(records)
record_df['index_name'] = index_name
record_dfs.append(record_df)
# Combine individual dataframes
all_records_df = pd.concat(record_dfs)
all_records_df = all_records_df.sort_values(by='index_name', ascending=False)
all_records_df = all_records_df.reset_index()
# Create columns where to store data later
all_records_df['success_status'] = 'not processed'
all_records_df['html'] = ''
حال، خوب است در صورت شکستن چیزی، یک کپی از داده ها را نگه دارید.
all_records_df.to_csv('all_records_df.csv', index=False)
سپس DataFrame را پردازش می کنیم تا فقط به چیزی که به آن علاقه داریم کاهش دهیم.
در اینجا، من فیلتر میکنم تا فقط صفحات آگهی کار به زبان انگلیسی را نگه دارم که دارای آن هستند /job/
رشته در URL
# Select english pages
df = all_records_df[all_records_df['languages'] == 'eng']
# Select oonly URLs that contain a certain string
df = df[df['url'].str.contains('/job/')]
df.head()
شما یک DataFrame با تمام داده های مورد نیاز برای دریافت محتوای هر صفحه وب دریافت خواهید کرد.
HTML را برای هر صفحه دانلود کنید
برای دانلود HTML برای هر صفحه وب، باید URL نام فایل رکورد warc را دریافت کنید که در این قالب است.
https://data.commoncrawl.org/crawl-data/CC-MAIN-2024-33/segments/1722641036895.73/warc/CC-MAIN-20240812092946-20240812122946-00210.warc.gz
شما مسیر این URL را در قسمت پیدا خواهید کرد filename
ستون دیتا فریم برای تجزیه آن، به مقدار آن نیز نیاز دارید offset
و length
ستون ها
تابع مهم در اینجا است fetch_single_record(warc_record_filename, offset, length)
جایی که warc_record_filename
ارزش در است filename
ستون
این یک فرآیند بسیار طولانی است که ممکن است چندین خطا داشته باشد (مثلاً چندگانه warc
فایل ها باز خواهند گشت ArchiveLoadFailed
مشکل، تنها راه حلی که پیدا کردم این بود که سعی کنم آن را واکشی کنم و در صورت وجود خطا آن را دور بریزم). به همین دلیل مهم است که استخراج خود را فقط به نوع صفحاتی که نیاز دارید محدود کنید.
پادمان ها
از آنجایی که این یک فرآیند طولانی است، خوب است که در صورت خراب شدن اسکریپت تدابیر امنیتی داشته باشید. به همین دلیل است که من داده ها را در یک فایل ترشی (به آموزش ترشی پایتون مراجعه کنید) ذخیره می کنم.
من همچنین از یک سری متغیر برای پیشرفت چاپ استفاده می کنم تا بفهمم چقدر طول می کشد تا تمام محتوا به دست آید.
# If pickle file exists, check for processed items
pickle_file = 'commcrawl_indeed.pkl'
processed_indices = load_processed_indices(pickle_file)
if processed_indices:
# Remove processed items
df = df[~df['index'].isin(processed_indices)]
# Create storage for later
successful = set()
results = {}
# Keep track of each row processed
i = 0
perc = 0
n_records = len(df)
print(f"Found {n_records} records for {target_url}")
mod = int(n_records * 0.01)
Extraction را اجرا کنید
اکنون میتوانیم با حلقه کردن هر ردیف و واکشی هر رکورد، تمام دادهها را استخراج کنیم.
برای سرعت بخشیدن به فرآیند، ما فقط یک نسخه از صفحه HTML (نه تاریخچه آن) را واکشی می کنیم. بنابراین اگر قبلاً HTML را در آخرین استخراج commoncrawl پیدا کردهایم، از نسخههای قبلی آن URL صرفنظر میکنیم.
# Reset index to help with looping
df.reset_index(drop=True,inplace=True)
for i in range(len(df)):
# Print every 1% process
if i % mod == 0:
print(f'{i} of {n_records}: {perc}%')
perc += 1
record_url = df.loc[i, 'url']
# Fetch only URLs that were not processed
# If it was already processed, skip URL
# (Helps speeding if you only need one version of the HTML, not its history)
if not record_url in successful:
length = int(df.loc[i, 'length'])
offset = int(df.loc[i, 'offset'])
warc_record_filename = df.loc[i, 'filename']
result = fetch_single_record(warc_record_filename, offset, length)
if not result:
df.loc[i,'success_status'] = 'invalid warc'
else:
df.loc[i,'success_status'] = 'success'
df.loc[i,'html'] = result
else:
df.loc[i,'success_status'] = 'previously processed'
# Add to pickle file
append_df_row_to_pickle(df.loc[i, :], pickle_file)
دادههای Commoncrawl Extraction را بخوانید
اکنون که همه چیز را استخراج کرده اید، می توانید داده ها را با استفاده از آن بخوانید read_pickle()
از pandas
.
commoncrawl_data = pd.read_pickle(pickle_file)
commoncrawl_data[
['url','filename','index_name','success_status','html']
].head()
در اینجا، می توانید تمام داده ها را در یک DataFrame دریافت کنید.
HTML را از Commoncrawl تجزیه کنید
اکنون که داده ها را دارید، ممکن است بخواهید فایل HTML را تجزیه کنید. می توانید این کار را با استفاده از BeautifulSoup در پایتون انجام دهید.
from bs4 import BeautifulSoup
# Select HTML from row 0
content = commoncrawl_data.loc[0, 'html']
# Parse in Beautiful soup
soup = BeautifulSoup(content, 'html.parser')
print(soup.find('title'))
FREELANCE PHOTOJOURNALIST - San Francisco, CA 94105 - Indeed.com
نحوه اجرای Commoncrawl با چندین موضوع
برای اجرای commoncrawl در چندین رشته، باید تابع را با استفاده از ماژول threading بپیچید.
import threading
n_threads = 3
threads = []
for i in range(n_threads):
pickle_file = f'data/commcrawl_expedia_hotel_information_th{i}.pkl'
thread_df = df[df.index % n_threads == i]
try:
t = threading.Thread(
target=cc_records_to_pkl,
args=[thread_df, pickle_file])
t.start()
except:
pass
threads.append
for thread in threads:
thread.join()
همانطور که گفته شد، هنگام اجرای روی رشته های جداگانه با مشکلاتی در ترشی کردن فایل ها مواجه خواهید شد. شما با ایجاد نقاط بازرسی موقت برای فایلهای ترشی خود و سپس ذخیره دادهها در فایلهای اصلی، آن را مدیریت خواهید کرد. تمام جزئیات در این کد است که در Github اضافه کردم.
این همان است، اکنون می توانید هر صفحه وب موجود در نرم افزار را با پایتون خراش دهید
استراتژیست سئو در Tripadvisor، Seek سابق (ملبورن، استرالیا). متخصص در سئو فنی. نویسنده در پایتون، بازیابی اطلاعات، سئو و یادگیری ماشین. نویسنده مهمان در SearchEngineJournal، SearchEngineLand و OnCrawl.