我设法从AirBnB的登录页(价格、名称、评级等)中抓取所有数据,我还知道如何使用循环来使用分页来从多个页面中抓取数据。
我想做的是为每个特定的列表抓取数据,即列表页面中的数据(描述、便利设施等)。
我想的是实现与分页相同的逻辑,因为我有一个带链接的list
,但我很难理解如何做到这一点。
以下是刮取链接的代码:
进口
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
import pandas as pd
import time
获取页面
airbnb_url = 'https://www.airbnb.com/s/Thessaloniki--Greece/homes?tab_id=home_tab&flexible_trip_lengths%5B%5D=one_week&refinement_paths%5B%5D=%2Fhomes&place_id=ChIJ7eAoFPQ4qBQRqXTVuBXnugk&query=Thessaloniki%2C%20Greece&date_picker_type=calendar&search_type=unknown'
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get(airbnb_url)
driver.maximize_window()
time.sleep(5)
报废链接
links = []
soup=BeautifulSoup(driver.page_source, 'lxml')
for card in soup.select('div[class="c4mnd7m dir dir-ltr"]'):
links.append('https://www.airbnb.com' + card.select_one('a[class="ln2bl2p dir dir-ltr"]')['href'])
我过去提取的";在哪里睡觉";部分是这个,但可能我用错了标签。
amenities = []
for url in links:
driver.get(url)
soup1 = BeautifulSoup(driver.page_source, 'lxml')
for amenity in soup1.select('div[class="t2pjd0h dir dir-ltr"]'):
amenities.append(amenity.select_one('div[class="_1r21qb98"]'))
我的第一个问题是,另一个问题是如果没有人,我如何才能获得每个列表的可用性。
非常感谢!
BeautifulSoup
在某些情况下更舒适,但并非在您的抓取过程中所需的所有方面-还应避免通过动态类选择元素,使用time
并切换到selenium waits
在所有列表页面上迭代时,我建议使用while-loop
来保持脚本的通用性,并在每次迭代中检查是否有下一个可用页面,否则使用break
作为循环。这消除了手动计数页面和条目以及使用静态range()
的需要。
try:
next_page = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR,'[data-section-id="EXPLORE_NUMBERED_PAGINATION:TAB_ALL_HOMES"] button + a'))).get_attribute('href')
except:
next_page = None
#### process your data
if next_page:
airbnb_url = next_page
else:
break
要刮去所有的便利设施,你必须通过点击按钮打开模式:
[i.text.split('n')[0] for i in WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR,'[data-testid="modal-container"] [id$="-row-title"]')))]
注意:为了避免其他元素点击时出现错误,请检查您是否必须处理cookie横幅
要提取卧室信息,请检查更多静态信息,如id或HTML结构,并检查元素是否可用-这行提取了本节中的所有信息,并从标题和值创建了一个dict
:
if soup.select_one('[data-section-id="SLEEPING_ARRANGEMENT_DEFAULT"] div+div'):
sleep_areas = list(soup.select_one('[data-section-id="SLEEPING_ARRANGEMENT_DEFAULT"] div+div').stripped_strings)
d.update(dict(zip(sleep_areas[0::2], sleep_areas[1::2])))
else:
d.update({'Bedroom':None})
示例
只是为了指明一个方向,并不是每个人都必须进行完全抓取,我在本例中将对象的抓取限制为每页urls[:1]
,只需删除[:1]
即可获得所有结果。
import pandas as pd
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = webdriver.ChromeOptions()
options.add_argument("--lang=en")
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=options)
airbnb_url = 'https://www.airbnb.com/s/Thessaloniki--Greece/homes?tab_id=home_tab&flexible_trip_lengths%5B%5D=one_week&refinement_paths%5B%5D=%2Fhomes&place_id=ChIJ7eAoFPQ4qBQRqXTVuBXnugk&query=Thessaloniki%2C%20Greece&date_picker_type=calendar&search_type=unknown'
driver.maximize_window()
data = []
while True:
driver.get(airbnb_url)
urls = list(set(a.get_attribute('href') for a in WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR,'[itemprop="itemListElement"] a')))))
try:
next_page = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR,'[data-section-id="EXPLORE_NUMBERED_PAGINATION:TAB_ALL_HOMES"] button + a'))).get_attribute('href')
except:
next_page = None
print('Scrape listings from page:' + str(next_page))
for url in urls[:1]:
driver.get(url)
WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR,'[data-section-id="AMENITIES_DEFAULT"] button'))).click()
soup = BeautifulSoup(driver.page_source)
d = {
'title':soup.h1.text,
'amenities':[i.text.split('n')[0] for i in WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR,'[data-testid="modal-container"] [id$="-row-title"]')))]
}
if soup.select_one('[data-section-id="SLEEPING_ARRANGEMENT_DEFAULT"] div+div'):
sleep_areas = list(soup.select_one('[data-section-id="SLEEPING_ARRANGEMENT_DEFAULT"] div+div').stripped_strings)
d.update(dict(zip(sleep_areas[0::2], sleep_areas[1::2])))
else:
d.update({'Bedroom':None})
data.append(d)
if next_page:
airbnb_url = next_page
else:
break
pd.DataFrame(data)
输出
title | 便利设施 | >公共空间tbody>||||
---|---|---|---|---|---|
0 | 8 NETFLIX BELGIUM HELLEXPO UNIVERSITY | ['','洗发水','必需品','衣架','熨斗','电视','空调','暖气','烟雾报警器','一氧化碳报警器','Wifi','专用工作区','烹饪基础','允许长期停留','不可用:物业上的安全摄像头','无法使用:厨房','不能使用:洗衣机','只能使用:私人入口'] | 1张沙发床 | nan | nan |
4 | ASOPOOSTUDIO | ["吹风机"、"洗发水"、"热水"、"必需品"、"、"床单"、"熨斗"、"电视"、"暖气"、"无线网络"、"厨房"、"冰箱"、"餐具和银器"、"免费街道停车场"、"电梯"、"带薪场外停车"、"允许长期入住"、"主人问候您"、"不可用:洗衣机"、《不可用:空调》、《不可使用:烟雾报警器》、《无法使用:一氧化碳报警器》、e: 私人入口'] | 1张沙发床 | 一张双人床 | |
14 | Aristotelous 8楼1层,视野开阔["热水"、"沐浴露"、"免费洗衣机-单元内"、"必需品"、"衣架"、"床单"、"熨斗"、"衣物烘干架"、"服装储藏室"、"电视"、"打包/旅行婴儿床-可根据要求提供"、"空调"、"暖气"、"无线网络"、"专用工作区"、"厨房"、"冰箱"、"微波炉"、"烹饪基础"、"餐具和银器"、"炉灶"、"热水壶"、"咖啡机","烤床单"、"咖啡"、"餐桌"、"私人露台或阳台"、"户外家具"、"带薪停车场"、"允许携带宠物"、"可以放行李"、"长期住宿"、"自助入住"、"锁箱"、"不可用:酒店内的安全摄像头"、"无法使用:烟雾报警器"、"不能使用:一氧化碳报警器",'不可用:私人入口'] | nan |
您想在每个详细信息页面的同时刮取总的列表页面。因此,每页包含20个总项目编号,也就是20个偏移项目,这意味着每页的偏移量每次增加20个列出的项目。我已经通过以下偏移量在起始url中进行了分页,然后第二次进入细节页面调用驱动程序和汤,从细节页面中,您必须提取所有必要的信息。
共有15个页面,单个页面有20个列表项目,因此总列表为15*20=300个列表。我刮了6页,意思是120个项目,意思是(0120,20)。你可以提取所有(0300,20)项目,只需将它们注入范围函数中。首先,测试我的代码,因为selenium有点慢,所以它会消耗更多的时间。
脚本:
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
import pandas as pd
import time
url = 'https://www.airbnb.com/s/Thessaloniki--Greece/homes?tab_id=home_tab&flexible_trip_lengths%5B%5D=one_week&refinement_paths%5B%5D=%2Fhomes&place_id=ChIJ7eAoFPQ4qBQRqXTVuBXnugk&query=Thessaloniki%2C%20Greece&date_picker_type=calendar&search_type=user_map_move&price_filter_input_type=0&ne_lat=40.66256734970964&ne_lng=23.003752862853986&sw_lat=40.59051931897441&sw_lng=22.892087137145978&zoom=13&search_by_map=true&federated_search_session_id=1ed21e1c-0c5e-4529-ab84-267361eac02b&pagination_search=true&items_offset={offset}§ion_offset=2'
data = []
for offset in range(0,120,20):
driver.get(url.format(offset=offset))
driver.maximize_window()
time.sleep(3)
soup=BeautifulSoup(driver.page_source, 'lxml')
detailed_pages = []
for card in soup.select('div[class="c4mnd7m dir dir-ltr"]'):
link = 'https://www.airbnb.com' + card.select_one('a[class="ln2bl2p dir dir-ltr"]').get('href')
detailed_pages.append(link)
for page in detailed_pages:
driver.get(page)
driver.maximize_window()
time.sleep(2)
soup2=BeautifulSoup(driver.page_source, 'lxml')
price = soup2.select_one('span._tyxjp1')
price = price.text if price else None
rating= soup2.select_one('span._12si43g')
rating = rating.text if rating else None
Bedroom_area = soup2.select_one('div[class="_1a5glfg"]')
Bedroom_area = Bedroom_area.text if Bedroom_area else None
place_offers= ', '.join([x.get_text(strip=True) for x in soup2.select('[class="sewcpu6 dir dir-ltr"]+div:nth-of-type(3) > div')])
data.append({
'place_offers': place_offers,
'price':price,
'rating':rating,
'Bedroom_area': Bedroom_area
})
df=pd.DataFrame(data)
print(df)
输出:
place_offers price rating Bedroom_area
0 $23 None None
1 $39 4.75 · None
2 $65 5.0 · None
3 Kitchen, Wifi, TV, Washer, Air conditioning, P... $90 4.92 · None
4 Wifi, TV, Air conditioning, Hair dryer, Paid p... $18 4.67 · None
.. ... ... ... ...
115 Kitchen, Wifi, Free street parking, Pets allow... $43 4.83 · 1 queen bed, 1 sofa bed
116 Kitchen, Wifi, HDTV with Netflix, Elevator, Ai... $38 4.73 · 1 double bed
117 Wifi, Dedicated workspace, TV, Elevator, AC - ... $34 4.85 · None
118 City skyline view, Kitchen, Wifi, Dedicated wo... $47 4.81 · None
119 Kitchen, Wifi, Free street parking, TV with Ne... $38 4.88 · None
[120 rows x 4 columns]