Làm thế nào để bạn cạo một bảng động trong python?

Trong chương này, chúng ta hãy tìm hiểu cách thực hiện quét web trên các trang web động và các khái niệm liên quan một cách chi tiết

Giới thiệu

Quét web là một nhiệm vụ phức tạp và độ phức tạp sẽ nhân lên nếu trang web động. Theo Kiểm toán toàn cầu về khả năng truy cập web của Liên hợp quốc, hơn 70% trang web có bản chất động và chúng dựa vào JavaScript cho các chức năng của chúng

Ví dụ trang web động

Chúng ta hãy xem một ví dụ về một trang web động và biết lý do tại sao nó khó cạo. Ở đây chúng ta sẽ lấy ví dụ tìm kiếm từ một trang web có tên http. //thí dụ. rút trích nội dung trang web. com/địa điểm/mặc định/tìm kiếm. Nhưng làm thế nào chúng ta có thể nói rằng trang web này có tính chất động?

import re
import urllib.request
response = urllib.request.urlopen['//example.webscraping.com/places/default/search']
html = response.read[]
text = html.decode[]
re.findall['[.*?]',text]

đầu ra

[ ]

Đầu ra ở trên cho thấy rằng trình quét ví dụ không thể trích xuất thông tin vì

phần tử chúng tôi đang cố tìm trống

Phương pháp thu thập dữ liệu từ các trang web động

Chúng tôi đã thấy rằng trình quét không thể lấy thông tin từ một trang web động vì dữ liệu được tải động bằng JavaScript. Trong những trường hợp như vậy, chúng ta có thể sử dụng hai kỹ thuật sau để lấy dữ liệu từ các trang web phụ thuộc JavaScript động -

  • Kỹ thuật đảo ngược JavaScript
  • Kết xuất JavaScript

Kỹ thuật đảo ngược JavaScript

Quy trình được gọi là kỹ thuật đảo ngược sẽ hữu ích và giúp chúng tôi hiểu cách dữ liệu được tải động bởi các trang web

Để thực hiện việc này, chúng tôi cần nhấp vào tab kiểm tra phần tử cho một URL được chỉ định. Tiếp theo, chúng tôi sẽ nhấp vào tab MẠNG để tìm tất cả các yêu cầu được thực hiện cho trang web đó bao gồm cả tìm kiếm. json với đường dẫn /ajax. Thay vì truy cập dữ liệu AJAX từ trình duyệt hoặc qua tab MẠNG, chúng ta cũng có thể làm điều đó với sự trợ giúp của tập lệnh Python sau -

import requests
url=requests.get['//example.webscraping.com/ajax/search.json?page=0&page_size=10&search_term=a']
url.json[] 

Thí dụ

Đoạn script trên cho phép chúng ta truy cập phản hồi JSON bằng cách sử dụng phương thức json của Python. Tương tự, chúng ta có thể tải xuống phản hồi chuỗi thô và bằng cách sử dụng json của python. loading, chúng ta cũng có thể tải nó. Chúng tôi đang làm điều này với sự trợ giúp của tập lệnh Python sau. Về cơ bản, nó sẽ quét tất cả các quốc gia bằng cách tìm kiếm chữ cái của bảng chữ cái 'a' và sau đó lặp lại các trang kết quả của các phản hồi JSON

import requests
import string
PAGE_SIZE = 15
url = '//example.webscraping.com/ajax/' + 'search.json?page={}&page_size={}&search_term=a'
countries = set[]
for letter in string.ascii_lowercase:
   print['Searching with %s' % letter]
   page = 0
   while True:
   response = requests.get[url.format[page, PAGE_SIZE, letter]]
   data = response.json[]
   print['adding %d records from the page %d' %[len[data.get['records']],page]]
   for record in data.get['records']:countries.add[record['country']]
   page += 1
   if page >= data['num_pages']:
      break
   with open['countries.txt', 'w'] as countries_file:
   countries_file.write['n'.join[sorted[countries]]] 

Sau khi chạy đoạn mã trên, chúng tôi sẽ nhận được đầu ra sau và các bản ghi sẽ được lưu trong tệp có tên quốc gia. txt

đầu ra

Searching with a
adding 15 records from the page 0
adding 15 records from the page 1
...

Kết xuất JavaScript

Trong phần trước, chúng tôi đã thực hiện kỹ thuật đảo ngược trên trang web về cách API hoạt động và cách chúng tôi có thể sử dụng nó để truy xuất kết quả trong một yêu cầu. Tuy nhiên, chúng ta có thể gặp phải những khó khăn sau khi thực hiện kỹ thuật đảo ngược -

  • Đôi khi các trang web có thể rất khó khăn. Ví dụ: nếu trang web được tạo bằng công cụ trình duyệt nâng cao, chẳng hạn như Google Web Toolkit [GWT], thì mã JS kết quả sẽ được tạo bằng máy và khó hiểu và kỹ sư đảo ngược

  • Một số framework cấp cao hơn như React. js có thể gây khó khăn cho kỹ thuật đảo ngược bằng cách trừu tượng hóa logic JavaScript vốn đã phức tạp

Giải pháp cho những khó khăn trên là sử dụng công cụ kết xuất trình duyệt phân tích cú pháp HTML, áp dụng định dạng CSS và thực thi JavaScript để hiển thị trang web

Thí dụ

Trong ví dụ này, để hiển thị Java Script, chúng ta sẽ sử dụng mô-đun Python quen thuộc Selenium. Mã Python sau đây sẽ hiển thị một trang web với sự trợ giúp của Selenium -

Tôi đã thử hôm nay và thực sự có thể cạo nó bằng Selenium webdriver để lưu trữ tất cả thông tin trong mã HTML tĩnh

Kết quả của html = driver.page_source là Lỗi UnicodeEncode [đã tìm thấy lỗi này bằng cách sử dụng print[html], dừng mọi thứ trừ khi nó được giải mã [cảm ơn bạn, ồ Google

].

Traceback [cuộc gọi gần đây nhất cuối cùng]
File “C:\dr_strange.py”, line 24, in
in [html]
File “C:\Python38-32\lib\encodings\cp1252.py”, line 19, in encode
trả lại codec. charmap_encode[input,self. lỗi,encoding_table][0]
UnicodeEncodeError. codec ‘charmap’ không thể mã hóa ký tự ‘\uff5c’ ở vị trí 2891. ký tự bản đồ để

At [stackoverflow][# python 3.x - Why I'm getting "UnicodeEncodeError: 'charmap' codec can't encode character '\u25b2' in position 84811: character maps to " error? - Stack Overflow] I found the following to decode:
html_dec = html.encode['utf-8'].decode['ascii', 'ignore']

Sau này, dữ liệu có thể được kiểm tra bằng beautifulsoup và chạy như một bùa mê
Tôi vẫn chưa cố đọc trang tiếp theo [và trang tiếp theo…vân vân], nhưng tôi đã đọc nó cho một trang web khác, vì vậy điều đó có thể xảy ra

Tôi phải đi làm bữa tối bây giờ, nhưng sẽ làm phần còn lại vào sáng mai [thêm dữ liệu khác, xem qua các trang, ghi vào khung dữ liệu]. [Nhưng xin vui lòng làm việc với nó và cập nhật cho chúng tôi về tiến độ của bạn

]

Đây là mã cho đến nay để có được quận của mọi giao dịch trên trang đầu tiên

import requests
from bs4 import BeautifulSoup
from selenium import webdriver
import time

# Instantiate lists to append data to for dataframe
District = []

# url of the page
url = "//oir.centanet.com/en/transaction/index?daterang=01%2F05%2F2019-29%2F04%2F2021&pageindex=0&pagesize=10&suggestlist=%5B%5D&posttype=0&districts=&sortby=0&usages=&pricetype=0&minprice=&maxprice=&renttype=0&minrent=&maxrent=&minarea=&maxarea=¢abldg=&sellindex=-1&rentindex=-1"

# initiating the webdriver. 
driver = webdriver.Chrome[r'C:\Program Files\Chromedriver\chromedriver.exe']
driver.get[url]

# Ensure page is fully loaded
time.sleep[3]

# Get the source of the current page
html = driver.page_source

# decode to work around error
# //stackoverflow.com/questions/62656579/why-im-getting-unicodeencodeerror-charmap-codec-cant-encode-character-u2
html_dec = html.encode['utf-8'].decode['ascii', 'ignore']

# Apply beautifulsoup to html variable
soup = BeautifulSoup[html_dec, "html.parser"]

# Make a list of all transactions
all_transactions = soup.find_all['div', class_='m-transaction-list-item']
# Iterate over each transaction and add data to list
for transaction in all_transactions:
    district_elem = transaction.find['p', class_="adress"].text.strip[]
    District.append[district_elem]

# Close webdriver
driver.close[]

thân não

Cảm ơn bạn đã trả lời, mã của bạn hoạt động tốt. Và tôi đã sửa lại một chút để trích xuất dữ liệu bảng, kèm theo màn hình in mà tôi đang tập trung vào dữ liệu bảng này

Chụp01485×859 46. 8KB


Tôi đã thêm mã dưới đây để bạn tham khảo.
# Make a list of all transactions
all_transactions = soup.find_all['div', class_='table_main']

for tr in soup.find_all["tr"]:
    for td in tr.find_all["td"]:
        print[td.text.strip[]]type or paste code here

Có cách nào tốt hơn để trích xuất dữ liệu vào pandas dataframe. Vì vậy, đầu ra dữ liệu giống như ở định dạng bảng bên dưới

Công cụ Ngày Sử dụng Quận Tên BĐS Tầng Đơn vị Diện tích Giá Đơn giá Nguồn
29/04/2021 Trung tâm thương mại công nghiệp toàn cầu Kwai Chung High – 879 sq. ft. cho thuê khoảng $7.911 @9 Tin tức thị trường
29/04/2021 Trung tâm thương mại công nghiệp toàn cầu Kwai Chung High – 916 sq. ft. cho thuê khoảng $8.244 @9 Tin tức thị trường

Tôi vẫn đang tìm cách kích hoạt “trang tiếp theo”


Nhân tiện, giải pháp của bạn là một hướng dẫn rất hay cho những người muốn trích xuất dữ liệu web bằng Webdriver + beautifulsoup.

cảm ơn vì lời khen của bạn. Tôi phải thừa nhận giải pháp của mình được hình thành bằng cách tìm kiếm và lấy các ví dụ và cộng chúng lại với nhau và khi nó hoạt động, tôi là một cậu bé hạnh phúc và tự hào

Mã của bạn rất đẹp nên hiệu quả hơn rất nhiều
Phương pháp của tôi là nối từng phần tử của mỗi giao dịch vào một danh sách riêng biệt và sau đó ghi chúng vào một khung dữ liệu, phương pháp này cồng kềnh hơn nhiều so với phương pháp của bạn, vì vậy cảm ơn vì đã chia sẻ điều đó
Tôi có 2 bổ sung nhỏ cho phần cuối cùng

# Create the dataframe from your table data
df = pd.DataFrame[rows[1:], columns=headers]  # removes empty row
print[df]
# output the result for investigation if any error
df.to_csv["CP01.csv", sep="*"]  # transaction price uses a comma, using an alternative seperator might be better

Về vấn đề này

  • Việc xóa hàng trống cũng có thể được thực hiện khi làm sạch dữ liệu trong khung dữ liệu và có thể 'chính xác' hơn, vì dù sao thì việc làm sạch nhiều hơn cũng có thể được áp dụng cho khung dữ liệu. Nó cũng có thể bị bỏ qua khi thêm dữ liệu vào danh sách hàng. Trường hợp này mình để ý thấy hàng trống khi phân tích code, chắc là khi code bình thường sẽ không thấy và rất phiền khi dọn dẹp dataframe
  • Không cần thiết phải phân tách thay thế khi không sử dụng csv cho bất kỳ thứ gì khác, đây chỉ là thói quen của tôi

Về “trang tiếp theo”. Tôi đã cạo một số trang web tĩnh có số trang trong URL của trang web, có thể được thao tác bằng vòng lặp for và đặt lại vào URL, do đó sẽ truy cập tất cả các trang. Tôi đã tìm số trang tối đa, vì vậy vòng lặp dừng ở đúng số
Tuy nhiên, trang web bạn đang tìm kiếm không có nó

, vì vậy tôi nhận thấy Selenium có thể tương tác với trang và có thể 'nhấp chuột' vào các phần tử. Vì tôi cũng đang học phần này khi đang di chuyển, nên tôi sẽ xem xét nó và đăng giải pháp của mình ngay khi tôi làm được.

Vì vậy, ở đây chúng tôi đi

Đối với trang web này, tôi đã nghĩ đến giải pháp sau [sau khi dùng thử và gặp rất nhiều lỗi về cách xác định mũi tên đến trang tiếp theo]. Nó được chèn sau khi thêm dữ liệu vào danh sách 'hàng. ’

________số 8_______

Sau đó lặp lại lấy nguồn của trang, giải mã và áp dụng lại bs4, v.v. , và cuối cùng tạo khung dữ liệu

Bây giờ, vì lại gần đến giờ ăn tối [hmmm…có vẻ như tôi đang ăn tối rất nhiều khi tôi trực tuyến

], những điều tôi chưa giải quyết được là.

  • Làm cách nào để dừng chương trình [nút không hoạt động trên trang cuối cùng, nhưng tôi chưa thử cách dừng chương trình mà không gặp lỗi]. Chúng tôi có thể xác định số lượng trang tối đa, đặt bộ đếm sau mỗi lần 'nhấp sang trang tiếp theo' và dừng khi

    # Make a list of all transactions
    all_transactions = soup.find_all['div', class_='table_main']
    
    for tr in soup.find_all["tr"]:
        for td in tr.find_all["td"]:
            print[td.text.strip[]]type or paste code here
    
    0 hoặc yêu cầu chương trình kiểm tra
    # Make a list of all transactions
    all_transactions = soup.find_all['div', class_='table_main']
    
    for tr in soup.find_all["tr"]:
        for td in tr.find_all["td"]:
            print[td.text.strip[]]type or paste code here
    
    1

  • Làm cách nào để làm cho các phần chính xác lặp lại mà không làm cho mã không đọc được [tôi nghĩ bằng cách xác định hàm và vòng lặp for hoặc while? ] Điều này có vẻ cơ bản với Python, nhưng tôi tập trung vào việc làm cho tất cả các phần hoạt động chính xác trước và chưa lo lắng về những thứ khác… . Bạn chỉ cần url và trình điều khiển một lần, tương tự với các tiêu đề của khung dữ liệu sau này, nhưng việc lấy html và thực sự cạo dữ liệu cần phải được lặp lại cho mỗi trang]

  • Bao nhiêu dữ liệu có thể được cạo và đưa vào bộ nhớ TRƯỚC KHI cần ghi nó vào một khung dữ liệu [do tốc độ chậm do các vấn đề về bộ nhớ hoặc có thể xảy ra sự cố sẽ làm cho tất cả dữ liệu được cạo lại biến mất]? Dữ liệu có thể được thêm vào sau khi cạo từng trang,
    Điều tương tự cũng xảy ra với việc thực sự lưu dữ liệu trong CSV, để có một bản sao trong trường hợp gặp sự cố trong quá trình cạo

Vì thế. nó có thể được thực hiện và tôi rất vui khi có thể hỗ trợ làm việc hướng tới một giải pháp. Bạn có thể giúp tôi bằng cách đưa ra các mẹo về cách giữ cho mã có thể đọc được và các gợi ý thiết thực về lượng dữ liệu sẽ được xử lý trước khi lưu trữ và lưu không?

Dự án tuyệt vời để làm việc, tôi rất vui với điều này

Dear Brian,

Tôi đã cố nối dữ liệu vào khung dữ liệu. Nhưng đầu ra rất lạ
Dưới đây là mã, bạn có thể thấy đầu ra sau khi nối thêm.

for c in range[2]:
    html = driver.page_source
    html_dec = html.encode['utf-8'].decode['ascii', 'ignore']
    soup = BeautifulSoup[html_dec, "html.parser"]
    header_soup = soup.findAll['th']
    row_soup = soup.find_all["tr"]
    rows = []
    for row in row_soup:
        row_data = []
        for cell in row.findAll['td']:
            #print[cell.text.strip[]]
            row_data.append[cell.text.strip[]]
        rows.append[row_data]
    print[str[c]]
    if c == 0:
        df = pd.DataFrame[rows[1:],columns=headers] #Remove empty row
        print[df]
    else:
        df2 = pd.DataFrame[rows[1:]]
        print["Below Append 2"]
        print[df2]
        mod_df = df.append[df2,sort=False]
        print["After Amend"]
        print[mod_df]
    df.to_csv["CP00.csv",sep="*"] ## output the result for investigation if any error
    #element = WebDriverWait[driver,20].until[EC.visibility_of_element_located[[By.XPATH,"//*[@id='__layout']/div/div[1]/div/div[3]/div[3]/div/div[2]/div[3]"]]]
    element = driver.find_element_by_xpath["//div[@class='right el-icon-arrow-right']"]
    element.click[]
    sleep[10]



# Close webdriver
driver.close[]

Sau khi nối thêm, bảng sẽ trở thành bên dưới.

        0           1           2  .. Unit Unit Price       Usage

0 NaN NaN NaN … – @9 Công nghiệp
1 NaN NaN NaN … – @9 Công nghiệp
2 NaN NaN NaN … – @9 Công nghiệp
3 NaN NaN NaN … – @9 Công nghiệp
4 NaN NaN NaN … – @9 Công nghiệp
5 NaN NaN NaN … – @29 Văn phòng
6 NaN NaN NaN … – @24 Công nghiệp
7 NaN NaN NaN … – @22 Văn phòng
8 NaN NaN NaN … 247 @16 ,327 Bán lẻ
9 NaN NaN NaN … 50 @15 ,625 Bán lẻ
0 29/04/2021 Văn phòng Sheung Wan … NaN NaN NaN
1 29/04/2021 Văn phòng Sheung Wan … NaN NaN NaN
2 29/04/2021 Văn phòng Sheung Wan … NaN NaN NaN
3 29/04/2021 Văn phòng Sheung Wan … NaN NaN NaN
4 29/04/2021 Industrial Kwun Tong … NaN NaN NaN
5 29/04/2021 Industrial Kwun Tong … NaN NaN NaN
6 29/04/2021 Carpark Tsuen Wan … NaN NaN NaN
7 29/04/2021 Industrial Kwun Tong … NaN NaN NaN
8 29/04/2021 Industrial Kwun Tong … NaN NaN NaN
9 29/04/2021 Văn phòng Bộ Hải quân … NaN NaN NaN

[20 hàng x 22 cột]

Chủ Đề