一、Headers反爬蟲 :Cookie、Referer、User-Agenthtml
解決方案: 經過F12獲取headers,傳給requests.get()方法python
二、IP限制 :網站根據IP地址訪問頻率進行反爬,短期內進制IP訪問mysql
解決方案: sql
一、構造本身IP代理池,每次訪問隨機選擇代理,常常更新代理池數據庫
二、購買開放代理或私密代理IPjson
三、下降爬取的速度api
三、User-Agent限制 :相似於IP限制網絡
解決方案: 構造本身的User-Agent池,每次訪問隨機選擇app
五、對查詢參數或Form表單數據認證(salt、sign)dom
解決方案: 找到JS文件,分析JS處理方法,用Python按一樣方式處理
六、對響應內容作處理
解決方案: 打印並查看響應內容,用xpath或正則作處理
一、pycharm進入方法 :Ctrl + r ,選中 Regex
二、處理headers和formdata
(.*): (.*)
"$1": "$2",
三、點擊 Replace All
目標: 抓取最新中華人民共和國縣以上行政區劃代碼
URL: http://www.mca.gov.cn/article/sj/xzqh/2019/ - 民政數據 - 行政區劃代碼
一、從民政數據網站中提取最新行政區劃代碼連接
最新的在上面,命名格式: 2019年X月中華人民共和國縣以上行政區劃代碼
import requests from lxml import etree import re url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/' headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'} html = requests.get(url, headers=headers).text parse_html = etree.HTML(html) article_list = parse_html.xpath('//a[@class="artitlelist"]') for article in article_list: title = article.xpath('./@title')[0] # 正則匹配title中包含這個字符串的連接 if title.endswith('代碼'): # 獲取到第1個就中止便可,第1個永遠是最新的連接 two_link = 'http://www.mca.gov.cn' + article.xpath('./@href')[0] print(two_link) break
二、從二級頁面連接中提取真實連接(反爬-響應網頁內容中嵌入JS,指向新的網頁連接)
# 爬取二級「假」連接
two_html = requests.get(two_link, headers=headers).text # 從二級頁面的響應中提取真實的連接(此處爲JS動態加載跳轉的地址) new_two_link = re.findall(r'window.location.href="(.*?)"', two_html, re.S)[0]
三、在數據庫表中查詢此條連接是否已經爬取,創建增量爬蟲
cursor.execute('select * from version') result = self.cursor.fetchall() if result: if result[-1][0] == two_link: print('已經是最新') else: # 有更新,開始抓取 # 將連接再從新插入version表記錄
四、代碼實現
import requests from lxml import etree import re import pymysql class GovementSpider(object): def __init__(self): self.url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/' self.headers = {'User-Agent': 'Mozilla/5.0'} # 建立2個對象 self.db = pymysql.connect('127.0.0.1', 'root', '123456', 'govdb', charset='utf8') self.cursor = self.db.cursor() # 獲取假連接 def get_false_link(self): html = requests.get(url=self.url, headers=self.headers).text # 此處隱藏了真實的二級頁面的url連接,真實的在假的響應網頁中,經過js腳本生成, # 假的連接在網頁中能夠訪問,可是爬取到的內容卻不是咱們想要的 parse_html = etree.HTML(html) a_list = parse_html.xpath('//a[@class="artitlelist"]') for a in a_list: # get()方法:獲取某個屬性的值 title = a.get('title') if title.endswith('代碼'): # 獲取到第1個就中止便可,第1個永遠是最新的連接 false_link = 'http://www.mca.gov.cn' + a.get('href') print("二級「假」連接的網址爲", false_link) break # 提取真連接 self.incr_spider(false_link) # 增量爬取函數 def incr_spider(self, false_link): self.cursor.execute('select url from version where url=%s', [false_link]) # fetchall: (('http://xxxx.html',),) result = self.cursor.fetchall() # not result:表明數據庫version表中無數據 if not result: self.get_true_link(false_link) # 可選操做: 數據庫version表中只保留最新1條數據 self.cursor.execute("delete from version") # 把爬取後的url插入到version表中 self.cursor.execute('insert into version values(%s)', [false_link]) self.db.commit() else: print('數據已經是最新,無須爬取') # 獲取真連接 def get_true_link(self, false_link): # 先獲取假連接的響應,而後根據響應獲取真連接 html = requests.get(url=false_link, headers=self.headers).text # 從二級頁面的響應中提取真實的連接(此處爲JS動態加載跳轉的地址) re_bds = r'window.location.href="(.*?)"' pattern = re.compile(re_bds, re.S) true_link = pattern.findall(html)[0] self.save_data(true_link) # 提取真連接的數據 # 用xpath直接提取數據 def save_data(self, true_link): html = requests.get(url=true_link, headers=self.headers).text # 基準xpath,提取每一個信息的節點列表對象 parse_html = etree.HTML(html) tr_list = parse_html.xpath('//tr[@height="19"]') for tr in tr_list: code = tr.xpath('./td[2]/text()')[0].strip() # 行政區劃代碼 name = tr.xpath('./td[3]/text()')[0].strip() # 單位名稱 print(name, code) # 主函數 def main(self): self.get_false_link() if __name__ == '__main__': spider = GovementSpider() spider.main()
特色
抓取
目標
F12抓包(XHR)
一、Request URL(基準URL地址) :https://movie.douban.com/j/chart/top_list?
二、Query String Paramaters(查詢參數)
# 查詢參數以下: type: 13 # 電影類型 interval_id: 100:90 action: '[{},{},{}]' start: 0 # 每次加載電影的起始索引值 limit: 20 # 每次加載的電影數量
json文件在如下地址:
基準URL地址+查詢參數
'https://movie.douban.com/j/chart/top_list?'+'type=11&interval_id=100%3A90&action=&start=20&limit=20'
代碼實現
import requests import time from fake_useragent import UserAgent class DoubanSpider(object): def __init__(self): self.base_url = 'https://movie.douban.com/j/chart/top_list?' self.i = 0 def get_html(self, params): headers = {'User-Agent': UserAgent().random} res = requests.get(url=self.base_url, params=params, headers=headers) res.encoding = 'utf-8' html = res.json() # 將json格式的字符串轉爲python數據類型 self.parse_html(html) # 直接調用解析函數 def parse_html(self, html): # html: [{電影1信息},{電影2信息},{}] item = {} for one in html: item['name'] = one['title'] # 電影名 item['score'] = one['score'] # 評分 item['time'] = one['release_date'] # 打印測試 # 打印顯示 print(item) self.i += 1 # 獲取電影總數 def get_total(self, typ): # 異步動態加載的數據 均可以在XHR數據抓包 url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'.format(typ) ua = UserAgent() html = requests.get(url=url, headers={'User-Agent': ua.random}).json() total = html['total'] return total def main(self): typ = input('請輸入電影類型(劇情|喜劇|動做):') typ_dict = {'劇情': '11', '喜劇': '24', '動做': '5'} typ = typ_dict[typ] total = self.get_total(typ) # 獲取該類型電影總數量 for page in range(0, int(total), 20): params = { 'type': typ, 'interval_id': '100:90', 'action': '', 'start': str(page), 'limit': '20'} self.get_html(params) time.sleep(1) print('爬取的電影的數量:', self.i) if __name__ == '__main__': spider = DoubanSpider() spider.main()
肯定URL地址及目標
要求與分析
一級頁面json地址(pageIndex在變,timestamp未檢查)
https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn
二級頁面地址(postId在變,在一級頁面中可拿到)
https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn
useragents.py文件
ua_list = [ 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)', ]
import time import json import random import requests from useragents import ua_list class TencentSpider(object): def __init__(self): self.one_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn' self.two_url = 'https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn' self.f = open('tencent.json', 'a') # 打開文件 self.item_list = [] # 存放抓取的item字典數據 # 獲取響應內容函數 def get_page(self, url): headers = {'User-Agent': random.choice(ua_list)} html = requests.get(url=url, headers=headers).text html = json.loads(html) # json格式字符串轉爲Python數據類型 return html # 主線函數: 獲取全部數據 def parse_page(self, one_url): html = self.get_page(one_url) item = {} for job in html['Data']['Posts']: item['name'] = job['RecruitPostName'] # 名稱 post_id = job['PostId'] # postId,拿postid爲了拼接二級頁面地址 # 拼接二級地址,獲取職責和要求 two_url = self.two_url.format(post_id) item['duty'], item['require'] = self.parse_two_page(two_url) print(item) self.item_list.append(item) # 添加到大列表中 # 解析二級頁面函數 def parse_two_page(self, two_url): html = self.get_page(two_url) duty = html['Data']['Responsibility'] # 工做責任 duty = duty.replace('\r\n', '').replace('\n', '') # 去掉換行 require = html['Data']['Requirement'] # 工做要求 require = require.replace('\r\n', '').replace('\n', '') # 去掉換行 return duty, require # 獲取總頁數 def get_numbers(self): url = self.one_url.format(1) html = self.get_page(url) numbers = int(html['Data']['Count']) // 10 + 1 # 每頁有10個推薦 return numbers def main(self): number = self.get_numbers() for page in range(1, 3): one_url = self.one_url.format(page) self.parse_page(one_url) # 保存到本地json文件:json.dump json.dump(self.item_list, self.f, ensure_ascii=False) self.f.close() if __name__ == '__main__': start = time.time() spider = TencentSpider() spider.main() end = time.time() print('執行時間:%.2f' % (end - start))