python 使用 selenium 从Google Jobs中抓取和提取工作数据并存储在Pandas DataFrame中

yruzcnhs  于 2023-02-18  发布在  Python
关注(0)|答案(1)|浏览(152)

我是StackOverflow的新手。如果帖子结构不好,请提前道歉。
我一直在学习用python抓取网页,作为我开发的一个业余项目的一部分,我尝试抓取Google Jobs的网页,并提取特定的数据存储在Pandas数据框中,我在python上使用 selenium 来实现这一点。
因此,我面临的主要挑战是找到一种方法,从搜索查询(url = Google Jobs)获得的网站上抓取所有的工作记录。这很困难,因为Google Jobs是动态加载的,即无限滚动,页面最初在侧面板上只加载10个结果。向下滚动时,每次滚动只会加载10个结果。
Website preview
我使用了selenium来帮助我完成这个任务,我想我可以通过指示selenium滚动到侧面板中与最后一个作业条目关联的列表元素(〈\li〉),并运行一个for循环来重复它,直到所有结果都加载到页面上,从而自动化滚动。
然后我只需要提取列表元素并将它们的文本存储到数据框中。
问题是每个职务条目都有3 - 6行文本,每行表示一些属性,如职务、公司名称或位置等,每个职务条目的行数不同,导致某些条目的行数比其他条目多。
Different number of lines for each job entry
因此,当我使用'\n'作为分隔符将文本拆分为python列表时,会导致列表长度不同,当我使用pd.DataFrame(list)生成 Dataframe 时,这就成了一个问题,会导致字段顺序混乱的记录。
Different Length Lists 😓
下面是我编写的代码:

#imports
import pandas as pd
import numpy as np
from serpapi import GoogleSearch
import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

#using selenium to launch and scroll through the Google Jobs page
url = "https://www.google.com/search?q=google+jobs+data+analyst&oq=google+jobs+data+analyst&aqs=chrome..69i57j69i59j0i512j0i22i30i625l4j69i60.4543j0j7&sourceid=chrome&ie=UTF-8&ibp=htl;jobs&sa=X&ved=2ahUKEwjXsv-_iZP9AhVPRmwGHX5xDEsQutcGKAF6BAgPEAU&sxsrf=AJOqlzWGHNISzgpAUCZBmQA1mWXXt3I7gA:1676311105893#htivrt=jobs&htidocid=GS94rKdYQqQAAAAAAAAAAA%3D%3D&fpstate=tldetail"
driver = webdriver.Chrome()
driver.get(url)
joblist =[]

#pointing to the html element to scroll to
elementxpath = '//*[@id="immersive_desktop_root"]/div/div[3]/div[1]/div[1]/div[3]/ul/li[10]'
element = driver.find_element(By.XPATH,elementxpath)
driver.execute_script('arguments[0].scrollIntoView(true)',element)
datas = driver.find_elements(By.XPATH,'//*

#capturing all the job list objects in the first page
[@id="immersive_desktop_root"]/div/div[3]/div[1]/div[1]/div[3]/ul/li') 
joblist.append([da.text for da in datas])

#adding 3s delay for website to load after scrolling before executing code
time.sleep(3)

#capturing all the job list objects in the second set of 10 results loaded after 1st scroll down
elementxpath = '//*[@id="VoQFxe"]/div/div/ul/li[10]'
element = driver.find_element(By.XPATH,elementxpath)
driver.execute_script('arguments[0].scrollIntoView(true)',element)
datas = driver.find_elements(By.XPATH,'//*[@id="VoQFxe"]/div/div/ul/li')
joblist.append([da.text for da in datas])
x=2
time.sleep(3)

#using a while loop to scroll and capture for the remaining scroll downs as element xpath is in iterable format unlike th previous 2 xpaths
while True:
    elementxpath = '//*[@id="VoQFxe"]/div['+str(1*x)+']/div/ul/li[10]'
    element = driver.find_element(By.XPATH,elementxpath)
    driver.execute_script('arguments[0].scrollIntoView(true)',element)
    x+=1
    time.sleep(3)
    datas = driver.find_elements(By.XPATH,'//*[@id="VoQFxe"]/div['+str(1*x)+']/div/ul/li')
    joblist.append([da.text for da in datas])
    if x>1000:
        break
    else:
        continue

#unpacking and cleaning captured values from joblist to a newlist of lists in the desired format for creating a dataframe
jlist = []
for n in joblist:
    for a in range(0,len(n)-1):
        if n[a]!='':
            jlist.append(n[a].split('\n'))

jobdf = pd.DataFrame(jlist)
jobdf.columns = ['Logo','Role', 'Company', 'Source','Posted','Full / Part Time', 'Waste']
jobdf

这是输出 Dataframe :
Jumbled mess 😶
文化的男人和女人们,我恳求你们的帮助,以获得一个有意义的有序数据框。谢谢!

kpbwa7wx

kpbwa7wx1#

通常你只能在简单的情况下使用.split('\n'),但在这种情况下是一个坏主意,一个更好的实践是为你想要抓取的每个元素使用一个唯一的xpath,一个用于logo,一个用于role,等等。
另一个好的实践是在开始时初始化字典,为每个要抓取的元素使用一个键,然后在循环作业时追加数据。
下面的代码就是这样做的,它没有优化速度,实际上它滚动到每个作业并抓取它,而最好的方法是抓取所有显示的作业的数据,然后滚动到底部,然后抓取所有新的作业并再次滚动,依此类推。

# import libraries...
# load webpage...

from selenium.common.exceptions import NoSuchElementException
xpaths = {
 'Logo'            :"./div[1]//img",
 'Role'            :"./div[2]",
 'Company'         :"./div[4]/div/div[1]",
 'Location'        :"./div[4]/div/div[2]",
 'Source'          :"./div[4]/div/div[3]",
 'Posted'          :"./div[4]/div/div[4]/div[1]",
 'Full / Part Time':"./div[4]/div/div[4]/div[2]",
}
data = {key:[] for key in xpaths}
jobs_to_do = 100
jobs_done = 0

while jobs_done < jobs_to_do:
    lis = driver.find_elements(By.XPATH, "//li[@data-ved]//div[@role='treeitem']/div/div")
    
    for li in lis[jobs_done:]:
        driver.execute_script('arguments[0].scrollIntoView({block: "center", behavior: "smooth"});', li)
        
        for key in xpaths:
            try:
                t = li.find_element(By.XPATH, xpaths[key]).get_attribute('src' if key=='Logo' else 'innerText')
            except NoSuchElementException:
                t = '*missing data*'
            data[key].append(t)
        
        jobs_done += 1
        print(f'{jobs_done=}', end='\r')
        time.sleep(.2)

然后通过运行pd.DataFrame(data),您会得到如下所示的结果

如图所示,“已发布”列中的一些值应该放在“全职/兼职”列中。这是因为有些工作没有发布时间的信息。我还注意到有些工作不仅有“已发布”和“全职/兼职”数据,还有“工资”。因此,您应该调整代码以考虑到这些事实。这并不容易,因为HTML对象没有针对每个元素的特定类,所以我认为您必须利用此图像中显示的svg符号(时钟、袋子和钞票

更新

我试着使用svg路径正确地刮取“已发布”、“全职/兼职”和“工资”,效果很好!以下是路径

xpaths = {
 'Logo'            :"./div[1]//img",
 'Role'            :"./div[2]",
 'Company'         :"./div[4]/div/div[1]",
 'Location'        :"./div[4]/div/div[2]",
 'Source'          :"./div[4]/div/div[3]",
 'Posted'          :".//*[name()='path'][contains(@d,'M11.99')]/ancestor::div[1]",
 'Full / Part Time':".//*[name()='path'][contains(@d,'M20 6')]/ancestor::div[1]",
 'Salary'          :".//*[name()='path'][@fill-rule='evenodd']/ancestor::div[1]"
}

用新路径替换旧路径,它将按预期工作,如下图所示

相关问题