三个小爬虫的最后一个是对拉勾网职位列表的爬取,当然这里没有考虑增量爬取,也没有考虑多线程爬取,仅仅是简单的把职位列表抓取下来并存储到数据库中。
设计思路如下:
-
访问拉勾网首页,获取默认的职位类型名称和链接
首页职位类型列表
-
模拟点击其中某一类型的职位,并对展现的职位列表信息进行抓取
职位列表
采用的工具包括urllib和selenium。
下面详细分解我是怎么做的(可能不一定对)
首先,是搞定首页。为什么要用urllib而不直接使用selenium呢,因为我发现直接用selenium的话,获取的页面信息中,只有默认的职位类型,没有后面的详细类型,必须要鼠标移动上去后才可以展现出后面的详细类型。(后来发现其实是隐藏在class是menu_sub这个div中的)


这样就比较麻烦了,无法获取完整的类型列表,当然我可以在selenium打开js功能,使用js来模拟鼠标移动上去等功能,这样我觉得比较麻烦,还不如直接用urllib去获取了。其次,我发现,如果是selenium模拟打开首页,拉勾网还会弹出一个页面让你选择具体的地方网站还是全国站,selenium还需要模拟点击一下全国站,当然这都不是难事,不过我嫌麻烦:)

好了,那么我们就用urllib来获取,这里先定义一个下载页面的工具函数:
def download(url, user_agent='balabala', num_retries=2):
print("Downloading: ", url)
print('num_retries: ', num_retries)
user_agent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
headers = {'User-agent': user_agent}
request = urllib.request.Request(url, headers=headers)
try:
html = urllib.request.urlopen(request).read()
except urllib.error.URLError as e:
print("Download Error: ", e.reason)
html = None
# 如果是服务器端问题的话就重复尝试,直到超过三次
if num_retries > 0:
if hasattr(e, 'code') and 500 <= e.code < 600:
return download(url, num_retries - 1)
html = html.decode('utf-8')
return html
user_agent我在代码中指定了,当然调用的时候可以传进来你自己定义的agent。num_retries代表的是重复访问的次数,意思是尝试访问这个网页url的次数,我这里使用了一个异常处理模块,如果访问URL的时候出错了,那么判断是否是5**的错误,这种错误是服务器内部错误,如果是这种错误,那么说明地址是对的,只是服务器出问题了,那么我重复访问num_retries次,如果还是没有信息那么只有范围None了,当然如果是400之类错误,那就是直接返回None的。
好了,现在有了访问函数了,我们就可以开始访问首页了。获取页面后用美味汤(BeautifulSoup)来解析,先prettify一下来看看,这里太长就不打印了。
data = download('https://www.lagou.com/')
soup = BeautifulSoup(data, "lxml")
# print(soup.prettify())
其实,用Chrome看一下的话,这个套路就清清楚楚了。很明显,就是class = "menu_sub dn"这个div先的dl节点列表

那接着就是干了,获取这个menu_sub的列表,遍历每个menu_sub,获取到每个menu中的每个链接a的名称以及地址即可。
menus = soup.select('.menu_sub')
for menu in menus:
hrefs = menu.select('a')
for href in hrefs:
print("%s, %s" % (href.text, href.attrs['href']))
结果如下:

好了,下一步就是访问这些职位类型的详情列表获取职位信息了。这里我换成了selenium访问具体的页面。
def crawl(url, browser):
print('抓取地址:%s' % url)
conn = initDB() # 初始化数据库连接
# 访问首页
browser.get(url)
browser.find_element_by_id('submit').click()
time.sleep(5) # 留出加载页面时间,同时也为了不给服务器太大压力
print(browser.current_url)
jobList = browser.find_elements_by_class_name('con_list_item')
这里有个小问题,如果用selenium访问职位列表URL,有时候会被拉勾网识别到,并列出初始化的职位列表,得不到我们请求的职位列表,所以我获取到页面的提交按钮,发送一个click()事件,等于是点了一下“搜索”按钮,返回的就一定是我们需要的职位列表了。

获取到数据后,我们就可以用selenium来解析了。可以看到每个职位列表被一个class为"con_list_item default_list"的li列表包围着

获取到职位列表后,我们遍历这个列表,取出相应职位名称,工作地点,企业名称,薪水,学历要求等信息,并存储到数据库中
for job in jobList:
jobName = job.find_element_by_tag_name('h3').text
location = job.find_element_by_class_name('add').text
money = job.find_element_by_class_name('money').text
education = job.find_element_by_class_name('li_b_l').text
education = education.replace(money, '')
company = job.find_element_by_class_name('company_name').text
print("jobName: %s, location: %s, money: %s, education: %s, company: %s" % (
jobName, location, money, education, company))
saveDB(conn, jobName, location, money, education, company)

接下来,还要一步,就是翻页。当遍历完当前的职位列表后,需要继续访问下一页的内容(如果有的话),我们查看一下“下一页”链接

好了,只要获取这个链接,并发送click事件就可以翻页了,如果到了最后一页是怎么样的呢?

可以看到,这里的class除了"pager_next"还多了一个"pager_next_disabled",当找到这个节点后,我们就可以跳出循环了。
当然,还有一种情况,就是第一页就没有"pager_next",为什么呢?因为这个类型的工作职位很少啊,连一页都填不满,就像下面图片中显示的那样,所以我们需要单独处理下,否则会报错。(这里我发现一个问题就是如果你直接访问前面menu_sub中获取的URL链接,可能再少的工作职位也会显示“下一页”,但是我是模拟点击“搜索”后的,就没有“下一页”按钮了,这些都是拉勾网的设计了,我们只能去适应它。)


发现区别了吗?
综合起来,翻页的代码如下:
try:
next_page = browser.find_element_by_class_name('pager_next') # 如果这句话抛出异常,说明没有下一页,工作很少,那么就直接返回,退出当前工作类别的爬取
except Exception as err:
return
while next_page:
try:
browser.find_element_by_class_name('pager_next_disabled') # 注意这里不能用if-else判断,否则在获取这个节点的时候就会报错
break
except NoSuchElementException as err:
next_page.click()
time.sleep(5) # 加载
jobList = browser.find_elements_by_class_name('con_list_item')
for job in jobList:
jobName = job.find_element_by_tag_name('h3').text
location = job.find_element_by_class_name('add').text
money = job.find_element_by_class_name('money').text
education = job.find_element_by_class_name('li_b_l').text
# print(type(education))
education = education.replace(money, '')
company = job.find_element_by_class_name('company_name').text
print("jobName: %s, location: %s, money: %s, education: %s, company: %s" % (
jobName, location, money, education, company))
saveDB(conn, jobName, location, money, education, company)
next_page = browser.find_element_by_class_name('pager_next')
time.sleep(10)
完整代码如下:
lagou.py,核心抓取函数
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
import time
import urllib.request
import urllib.error
import re
from selenium.webdriver.common.touch_actions import TouchActions
from selenium.webdriver.common.keys import Keys
from bs4 import BeautifulSoup
import psycopg2
def initDB(): # 初始化数据连接
conn = psycopg2.connect(database="spider", user="postgres", password="kfbserver203.", host="127.0.0.1", port="5432")
return conn
def closeDB(conn): # 关闭数据库连接
conn.close()
def saveDB(conn, jobName, location, money, education, company): # 存储数据
cur = conn.cursor()
cur.execute(
"INSERT INTO job (job_name,location,money,education,company) VALUES ('" + jobName + "','" + location + "','" + money + "','" + education + "','" + company + "')")
conn.commit()
def crawl(url, browser):
print('抓取地址:%s' % url)
conn = initDB()
# 访问首页
# browser.get('https://www.lagou.com/zhaopin/shenduxuexi/?filterOption=3')
browser.get(url)
result = []
# href_e = browser.find_element_by_link_text('全国站')
browser.find_element_by_id('submit').click()
time.sleep(5)
print(browser.current_url)
jobList = browser.find_elements_by_class_name('con_list_item')
for job in jobList:
jobName = job.find_element_by_tag_name('h3').text
location = job.find_element_by_class_name('add').text
money = job.find_element_by_class_name('money').text
education = job.find_element_by_class_name('li_b_l').text
# print(type(education))
education = education.replace(money, '')
company = job.find_element_by_class_name('company_name').text
print("jobName: %s, location: %s, money: %s, education: %s, company: %s" % (
jobName, location, money, education, company))
saveDB(conn, jobName, location, money, education, company)
try:
next_page = browser.find_element_by_class_name('pager_next') # 如果这句话抛出异常,说明没有下一页,工作很少,那么就直接返回,退出当前工作类别的爬取
except Exception as err:
return
while next_page:
try:
browser.find_element_by_class_name('pager_next_disabled')
break
except NoSuchElementException as err:
next_page.click()
time.sleep(5) # 加载
jobList = browser.find_elements_by_class_name('con_list_item')
for job in jobList:
jobName = job.find_element_by_tag_name('h3').text
location = job.find_element_by_class_name('add').text
money = job.find_element_by_class_name('money').text
education = job.find_element_by_class_name('li_b_l').text
# print(type(education))
education = education.replace(money, '')
company = job.find_element_by_class_name('company_name').text
print("jobName: %s, location: %s, money: %s, education: %s, company: %s" % (jobName, location, money, education, company))
saveDB(conn, jobName, location, money, education, company)
next_page = browser.find_element_by_class_name('pager_next')
time.sleep(10)
closeDB(conn) # 关闭数据库
lagou2.py,抓取职位类型,创建selenium webdriver对象,调用抓取程序
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
import time
import urllib.request
import urllib.error
import urllib.parse
from http import cookiejar
import re
from selenium.webdriver.common.touch_actions import TouchActions
from selenium.webdriver.common.keys import Keys
from bs4 import BeautifulSoup
import lagou
'''
获取各种默认搜索关键字和链接
'''
# 直接下载页面
def download(url, user_agent='wswp', num_retries=2):
print("Downloading: ", url)
print('num_retries: ', num_retries)
user_agent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
headers = {'User-agent': user_agent}
request = urllib.request.Request(url, headers=headers)
try:
html = urllib.request.urlopen(request).read()
except urllib.error.URLError as e:
print("Download Error: ", e.reason)
html = None
# 如果是服务器端问题的话就重复尝试,直到超过三次
if num_retries > 0:
if hasattr(e, 'code') and 500 <= e.code < 600:
return download(url, num_retries - 1)
html = html.decode('utf-8')
return html
data = download('https://www.lagou.com/')
soup = BeautifulSoup(data, "lxml")
# print(soup.prettify())
menus = soup.select('.menu_sub')
browser = webdriver.Chrome("C://Drivers/chromedriver_win32/chromedriver.exe")
for menu in menus:
hrefs = menu.select('a')
for href in hrefs:
print("%s, %s" % (href.text, href.attrs['href']))
lagou.crawl(href.attrs['href'], browser)


网友评论