<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          實(shí)戰(zhàn)|Python爬取字節(jié)跳動(dòng)1W+招聘信息

          共 7167字,需瀏覽 15分鐘

           ·

          2021-01-23 08:41

          ↑?關(guān)注 + 星標(biāo)?,每天學(xué)Python新技能

          后臺(tái)回復(fù)【大禮包】送你Python自學(xué)大禮包


          今天我們打算爬取一下字節(jié)跳動(dòng)的招聘信息:

          我們打開(kāi)開(kāi)發(fā)者工具并訪問(wèn):

          https://jobs.bytedance.com/experienced/position?keywords=&category=&location=&project=&type=&job_hot_flag=¤t=1&limit=10

          這次訪問(wèn)監(jiān)控到的數(shù)據(jù)很多,其中這個(gè)posts接口才有我們需要的json數(shù)據(jù):

          觀察響應(yīng)頭發(fā)現(xiàn)一個(gè)重要參數(shù)csrf:

          說(shuō)明字節(jié)跳動(dòng)的網(wǎng)站具備csrf校驗(yàn)的功能,后文將再介紹如何獲取到這個(gè)csrf的token。

          查看請(qǐng)求參數(shù):

          參數(shù)包裝函數(shù)

          為了正常爬取時(shí)的方便,我們需要先將上面需要的參數(shù),組織成python能夠識(shí)別的字典形式。直接復(fù)制粘貼有很多需要加雙引號(hào)的地方,但我們可以編程解決這個(gè)問(wèn)題。

          首先,定義一個(gè)處理函數(shù):

          import re


          def warp_heareder(s):
          print("{")
          lines = s.splitlines()
          for i, line in enumerate(lines):
          k, v = line.split(": ")
          if re.search("[a-zA-Z]", k):
          k = f'"{k}"'
          if re.search("[a-zA-Z]", v):
          v = f'"{v}"'
          print(f" {k}: {v},")
          print("}")

          處理請(qǐng)求頭:

          處理post請(qǐng)求數(shù)據(jù):


          csrf校驗(yàn)值獲取

          首先,清空cookie:

          然后刷新頁(yè)面,查看網(wǎng)絡(luò)請(qǐng)求的抓包情況:

          找啊找,終于找到了一個(gè)set-cookie的響應(yīng)頭,而且這個(gè)設(shè)置cookie參數(shù)包括了csrf的設(shè)置。那么這個(gè)接口我們就可以用來(lái)作為獲取csrf校驗(yàn)值的接口。

          使用session保存響應(yīng)頭設(shè)置的cookie:

          import requests

          session = requests.session()
          headers = {
          'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
          'Origin': 'https://jobs.bytedance.com',
          'Referer': f'https://jobs.bytedance.com/experienced/position?keywords=&category=&location=&project=&type=&job_hot_flag=¤t=1&limit=10'
          }
          data = {
          "portal_entrance": 1
          }
          url = "https://jobs.bytedance.com/api/v1/csrf/token"
          r = session.post(url, headers=headers, data=data)
          r

          結(jié)果:


          查看獲取到的cookie:

          cookies = session.cookies.get_dict()
          cookies

          結(jié)果:

          {'atsx-csrf-token': 'RDTEznQqdr3O3h9PjRdWjfkSRW79K_G16g85FrXNxm0%3D'}

          顯然這個(gè)token相對(duì)真實(shí)需要的存在url編碼,現(xiàn)在對(duì)它進(jìn)行url解碼:

          from urllib.parse import unquote

          unquote(cookies['atsx-csrf-token'])

          結(jié)果:

          'RDTEznQqdr3O3h9PjRdWjfkSRW79K_G16g85FrXNxm0='


          開(kāi)始爬取第一頁(yè)的數(shù)據(jù)

          有了token我們就可以順利的直接訪問(wèn)接口了:

          import requests
          import json

          headers = {
          "Accept": "application/json, text/plain, */*",
          "Host": "jobs.bytedance.com",
          "Origin": "https://jobs.bytedance.com",
          "Referer": "https://jobs.bytedance.com/experienced/position?keywords=&category=&location=&project=&type=&job_hot_flag=¤t=1&limit=10",
          "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
          "x-csrf-token": unquote(cookies['atsx-csrf-token']),
          }
          data = {
          "job_category_id_list": [],
          "keyword": "",
          "limit": 10,
          "location_code_list": [],
          "offset": 0,
          "portal_entrance": 1,
          "portal_type": 2,
          "recruitment_id_list": [],
          "subject_id_list": []
          }
          url = "https://jobs.bytedance.com/api/v1/search/job/posts"
          r = session.post(url, headers=headers, data=json.dumps(data))
          r

          結(jié)果:


          響應(yīng)碼是200,說(shuō)明已經(jīng)順利通過(guò)了校驗(yàn),現(xiàn)在查看一下數(shù)據(jù)結(jié)構(gòu):

          r.json()

          結(jié)果:


          使用Pandas對(duì)json數(shù)據(jù)進(jìn)行處理

          import pandas as pd

          df = pd.DataFrame(r.json()['data']['job_post_list'])
          df.head(3)

          結(jié)果:

          然后我們對(duì)各列提取出我們需要的數(shù)據(jù):

          df.city_info = df.city_info.str['name']
          df.recruit_type = df.recruit_type.str['parent'].str['name']
          tmp = []
          for x in df.job_category.values:
          if x['parent']:
          tmp.append(f"{x['parent']['name']}-{x['name']}")
          else:
          tmp.append(x['name'])
          df.job_category = tmp
          df.publish_time = df.publish_time.apply(lambda x: pd.Timestamp(x, unit="ms"))
          df.head(2)

          結(jié)果:

          再刪除一些,明顯沒(méi)有任何用的列:

          df.drop(columns=['sub_title', 'job_hot_flag', 'job_subject'], inplace=True)
          df.head()

          結(jié)果:


          爬取字節(jié)跳動(dòng)全部職位信息

          有了上面的測(cè)試基礎(chǔ),我們就可以組織一下完整的爬取代碼:

          import requests
          from urllib.parse import unquote
          import pandas as pd
          import time
          import os

          session = requests.session()
          page = 1500
          headers = {
          'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
          'Origin': 'https://jobs.bytedance.com',
          'Referer': f'https://jobs.bytedance.com/experienced/position?keywords=&category=&location=&project=&type=&job_hot_flag=¤t=1&limit={page}'
          }
          data = {
          "portal_entrance": 1
          }
          url = "https://jobs.bytedance.com/api/v1/csrf/token"
          r = session.post(url, headers=headers, data=data)
          cookies = session.cookies.get_dict()

          url = "https://jobs.bytedance.com/api/v1/search/job/posts"
          headers["x-csrf-token"] = unquote(cookies["atsx-csrf-token"])
          data = {
          "job_category_id_list": [],
          "keyword": "",
          "limit": page,
          "location_code_list": [],
          "offset": 0,
          "portal_entrance": 1,
          "portal_type": 2,
          "recruitment_id_list": [],
          "subject_id_list": []
          }
          for i in range(11):
          print(f"準(zhǔn)備爬取第{i}頁(yè)")
          data["offset"] = i*page
          r = None
          while not r:
          try:
          r = session.post(url, headers=headers,
          data=json.dumps(data), timeout=3)
          except Exception as e:
          print("訪問(wèn)超時(shí)!等待5s", e)
          time.sleep(5)
          df = pd.DataFrame(r.json()['data']['job_post_list'])
          if df.shape[0] == 0:
          print("爬取完畢?。?!")
          break
          df.city_info = df.city_info.str['name']
          df.recruit_type = df.recruit_type.str['parent'].str['name']
          tmp = []
          for x in df.job_category.values:
          if x['parent']:
          tmp.append(f"{x['parent']['name']}-{x['name']}")
          else:
          tmp.append(x['name'])
          df.job_category = tmp
          df.publish_time = df.publish_time.apply(
          lambda x: pd.Timestamp(x, unit="ms"))
          df.drop(columns=['sub_title', 'job_hot_flag', 'job_subject'], inplace=True)
          df.to_csv("bytedance_jobs.csv", mode="a", header=not os.path.exists("bytedance_jobs.csv"), index=False)
          print(",".join(df.title.head(10)))
          # 對(duì)結(jié)果去重
          df = pd.read_csv("bytedance_jobs.csv")
          df.drop_duplicates(inplace=True)
          df.to_csv("bytedance_jobs.csv", index=False)
          print("共爬取", df.shape[0], "行無(wú)重復(fù)數(shù)據(jù)")

          結(jié)果:

          僅7.3秒爬完了字節(jié)跳動(dòng)1W+以上的職位信息。

          可以讀取看看:

          import pandas as pd

          df = pd.read_csv("bytedance_jobs.csv")
          df

          結(jié)果:

          有1萬(wàn)個(gè)以上的職位信息。

          補(bǔ)充資料

          CSRF的含義

          CSRF(Cross-site request forgery)也被稱為 one-click attack或者 session riding,中文全稱是叫「跨站請(qǐng)求偽造」。一般來(lái)說(shuō),攻擊者通過(guò)偽造用戶的瀏覽器的請(qǐng)求,向訪問(wèn)一個(gè)用戶自己曾經(jīng)認(rèn)證訪問(wèn)過(guò)的網(wǎng)站發(fā)送出去,使目標(biāo)網(wǎng)站接收并誤以為是用戶的真實(shí)操作而去執(zhí)行命令。常用于盜取賬號(hào)、轉(zhuǎn)賬、發(fā)送虛假消息等。攻擊者利用網(wǎng)站對(duì)請(qǐng)求的驗(yàn)證漏洞而實(shí)現(xiàn)這樣的攻擊行為,網(wǎng)站能夠確認(rèn)請(qǐng)求來(lái)源于用戶的瀏覽器,卻不能驗(yàn)證請(qǐng)求是否源于用戶的真實(shí)意愿下的操作行為。

          CSRF的攻擊原理

          比如,博客網(wǎng)站A的后臺(tái)存在一個(gè)添加文章的功能,為方便說(shuō)明,假設(shè)它是個(gè)get請(qǐng)求,如/admin/add?title=標(biāo)題&body=內(nèi)容。要提交這個(gè)請(qǐng)求時(shí),會(huì)判斷用戶是否已經(jīng)登錄,如果沒(méi)登錄則會(huì)自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面,只有管理員有權(quán)限登錄。所以,攻擊者即使知道該請(qǐng)求路徑,也過(guò)不了登錄這關(guān)。

          但是攻擊者在自己的網(wǎng)站或支持富文本編輯的論壇網(wǎng)站B上評(píng)論如下的內(nèi)容:


          當(dāng)某個(gè)用戶打開(kāi)網(wǎng)站B時(shí),如果對(duì)于網(wǎng)站A的登錄后臺(tái)的session還有效,那么他就會(huì)自動(dòng)向博客網(wǎng)站A后臺(tái)發(fā)送添加文章的請(qǐng)求,完成攻擊者的目的。這個(gè)過(guò)程中,攻擊者不需要拿到用戶的cookie就可以完成攻擊。

          當(dāng)然博客網(wǎng)站A可以把校驗(yàn)改成post請(qǐng)求來(lái)避免來(lái)著img標(biāo)簽帶來(lái)的攻擊,但仍然無(wú)法避免通過(guò)javascript模擬post請(qǐng)求帶來(lái)的攻擊(將上面html代碼改成JavaScript代碼即可)。

          防范CSRF攻擊的方法

          開(kāi)啟token驗(yàn)證:CSRF 攻擊之所以能夠成功,是因?yàn)楹诳涂梢酝耆珎卧煊脩舻恼?qǐng)求,該請(qǐng)求中所有的用戶驗(yàn)證信息都是存在于cookie中,因此黑客可以在不知道這些驗(yàn)證信息的情況下直接利用用戶自己的cookie 來(lái)通過(guò)安全驗(yàn)證。要抵御 CSRF,關(guān)鍵在于在請(qǐng)求中放入黑客所不能偽造的信息,并且該信息不存在于 cookie 之中??梢栽?HTTP 請(qǐng)求中以參數(shù)的形式加入一個(gè)隨機(jī)產(chǎn)生的 token,并在服務(wù)器端建立一個(gè)攔截器來(lái)驗(yàn)證這個(gè) token,如果請(qǐng)求中沒(méi)有token或者 token 內(nèi)容不正確,則認(rèn)為可能是 CSRF 攻擊而拒絕該請(qǐng)求。token 在用戶登陸后產(chǎn)生并放于session之中,然后在每次請(qǐng)求時(shí)把token 從 session 中拿出,與請(qǐng)求中的 token 進(jìn)行比對(duì)。

          一些問(wèn)題的解釋

          字節(jié)職位的服務(wù)本身并不需要防范CSRF攻擊,只是因?yàn)榭蚣苣J(rèn)開(kāi)啟了這項(xiàng)認(rèn)證,我們也就只需根據(jù)規(guī)則完成這個(gè)認(rèn)證,證明我跟上一訪問(wèn)是同一個(gè)人即可。

          為了使python的訪問(wèn)能緩存cooike相關(guān)的信息,所以我使用了session會(huì)話,響應(yīng)頭設(shè)置的cookie都會(huì)保留下來(lái)。

          我使用r = session.post(url, headers=headers, data=json.dumps(data))而不是直接使用r = session.post(url, headers=headers, data=data)的原因是字節(jié)跳動(dòng)nginx服務(wù)器json文本校驗(yàn)的原因,requests庫(kù)內(nèi)部將字典對(duì)象轉(zhuǎn)為json文本的結(jié)果無(wú)法被nginx解析,但直接使用json庫(kù)將字典對(duì)象轉(zhuǎn)換成功的json文本卻可以被nginx服務(wù)器解析通過(guò)(不信可以自己嘗試)。

          今天的分享就到這里了,喜歡就給個(gè)“在看”再走吧!



          長(zhǎng)按上方二維碼后回復(fù):
          富爸爸,免費(fèi)獲取富爸爸電子書全套。??

          推薦閱讀


          1. 火了!開(kāi)源的Python搶票神器,過(guò)年回家就看這一波了!

          2. 為什么 Django 框架比Flask框架更流行?

          3. 因匿名評(píng)論“B站能睡小姐姐,我睡了四個(gè)”,B站把脈脈給告了

          4. 美團(tuán)App偷偷給用戶開(kāi)通金融貸款?!網(wǎng)友:1.5元就讓我貸款?

          5. 一文講透Python數(shù)據(jù)可視化技巧


          點(diǎn)分享
          點(diǎn)收藏
          點(diǎn)點(diǎn)贊
          點(diǎn)在看
          瀏覽 54
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青青草视频在线免费 | 国产AV高清 | 在线色国产 | jzzjzz日本丰满成熟少妇 | 欧美性爱网站免费 |