0.項目背景
導師的一篇論文需要用到包含某一個關鍵字Github上的代碼片段,所以我寫了一個爬蟲項目將github上面包含某一關鍵字的代碼鏈接全部爬取出來,並存入csv文件中。
1.開發環境和工具模塊
python版本:python 3.6
開發用的IDE:pycharm
所用的第三方庫: 爬蟲:requests + BeautifulSoup
github官方API: https://developer.github.com/v3/search/#search-code //這是官方的API用來搜索,得到的是一個Json文件//
2.開發思路
本來的想法是直接用github官方的API直接得到一個JSON文件然後再去解析,但是13年以後,search-code的這個API就不能在全部的公共庫中去搜索,搜索時,必須要指定特定的用戶或者機構或者是項目庫。這個是它的官方說明: https://developer.github.com/changes/2013-10-18-new-code-search-requirements/
所以要換一個思路,我的想法是先用爬蟲爬取所有包含某寫關鍵字代碼的庫名和作者名,然後用這些庫名和某一關鍵字作爲參數去調用github的官方API,通過解析返回的JSON文檔,得到相關代碼的鏈接和一些其他的信息。
3.項目分析
3.1 獲取包含某關鍵字代碼的庫名
def parse_keyword(self, keyword):
# 解析登陸以後的頁面,篩選出包含這個關鍵字的python代碼
user_repositorys = set() # 集合用來存放作者名和庫名
for i in range(101): #github上只顯示前一百頁的信息
url = "https://github.com/search?l=Python&p={id}&q={keyword}&type=Code".format(id=i+1, keyword=keyword) # 循環的爬取頁面信息
#需要登錄驗證
resp = self.session.get(url=url, headers=self.login_headers, verify=False)
soup = BeautifulSoup(resp.text, "lxml")#解析信息
#通過標籤可以篩序信息
pattern = soup.find_all('a', class_='text-bold')
for item in pattern: #將庫名和作者名存入集合之中
print(item.string)
user_repositorys.add(item.string)
上面代碼主要是有兩個問題要解決,第一個是github上爬取公共庫需要登錄驗證,我參考的是這位作者的方法:https://blog.csdn.net/killeri/article/details/86093665
代碼如下:
class github_crawl(): def __init__(self): # 初始化一些必要的參數 self.login_headers = { "Referer": "https://github.com/", "Host": "github.com", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" } self.logined_headers = { "Referer": "https://github.com/login", "Host": "github.com", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" } self.login_url = "https://github.com/login" self.post_url = "https://github.com/session" self.session = requests.Session() def parse_loginPage(self): # 對登陸頁面進行爬取,獲取token值 html = self.session.get(url=self.login_url, headers=self.login_headers, verify=False) Soup = BeautifulSoup(html.text, "lxml") token = Soup.find("input", attrs={"name": "authenticity_token"}).get("value") return token # 獲得了登陸的一個參數 def login(self, user_name, password): # 傳進必要的參數,然後登陸 post_data = { "commit": "Sign in", "utf8": "✓", "authenticity_token": self.parse_loginPage(), "login": user_name, "password": password } } logined_html = self.session.post(url=self.post_url, data=post_data, headers=self.logined_headers, verify=False) if logined_html.status_code == 200: self.parse_keyword() # 調用parse_keyword,獲得關於這個關鍵字的庫名 if __name__ == "__main__": x = github_crawl() x.login("user", "password")
第二問題是,是找到網頁中關於作者和庫名顯示的標籤
這個問題比較好解決:
不難發現,他的html的特點,所以用soup.findall函數:
pattern = soup.find_all('a', class_='text-bold')
到此爲止,我們就已經把包含某個關鍵字的的庫名全部爬取下來了。
3.2 調用github上的官方API
得到了包含某個關鍵字的庫名以後,我們就可是使用github的官方API了,他的官方API使用起來非常方便,就是一個網頁鏈接的形式,構造好了以後,就會返回一個json文件。例如:
https://api.github.com/search/code?q=df.applymap+in:file+language:python+repo:rvpatel92/HW3
//表示在rvpatel92/HW3這個庫裏面包含df.applymap這個關鍵字的代碼片段
把這個網址輸進去,得到結果如下,剩下的工作就是解析這個json文件
代碼如下
def get_results(self, repository, keyword): # 用Github的官方API爬取數據,解析json
url = "https://api.github.com/search/code?q={keyword}+in:file+language:python+repo:{w}".format(w=repository, keyword=keyword)#只是搜索語言爲python的代碼
req = Request(url, headers=self.headers) #需要token認證
response = urlopen(req).read()
results = json.loads(response.decode())
for item in results['items']:
repo_url = item["repository"]["html_url"]#得到項目庫的鏈接
file_path = item['html_url']#得到代碼的鏈接
fork = item["repository"]["fork"]#是否引用
self.loader_csv(repo_url, file_path, fork, keyword)
def loader_csv(self, repo_url, file_path, fork, keyword): #寫入csv文件
with open("F:\\task\\github.csv", "a") as csv_file:
writer = csv.writer(csv_file)
writer.writerow([keyword, repo_url, file_path])
csv_file.close()
到此整個項目的任務算是完成了。
4.項目完整代碼
class github_crawl():
def __init__(self):
# 初始化一些必要的參數
self.login_headers = {
"Referer": "https://github.com/",
"Host": "github.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
}
self.logined_headers = {
"Referer": "https://github.com/login",
"Host": "github.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
}
self.headers = {
'User-Agent': 'Mozilla/5.0',
'Authorization': 'token 80531720f7835b5861e87812aa773256086498d6',#換上自己的token認證
'Content-Type': 'application/json',
'Accept': 'application/json'
}
self.login_url = "https://github.com/login"
self.post_url = "https://github.com/session"
self.session = requests.Session()
def parse_loginPage(self):
# 對登陸頁面進行爬取,獲取token值
html = self.session.get(url=self.login_url, headers=self.login_headers, verify=False)
Soup = BeautifulSoup(html.text, "lxml")
token = Soup.find("input", attrs={"name": "authenticity_token"}).get("value")
return token
# 獲得了登陸的一個參數
def login(self, user_name, password,keyword):
# 傳進必要的參數,然後登陸
post_data = {
"commit": "Sign in",
"utf8": "✓",
"authenticity_token": self.parse_loginPage(),
"login": user_name,
"password": password
}
logined_html = self.session.post(url=self.post_url, data=post_data, headers=self.logined_headers, verify=False)
if logined_html.status_code == 200:
self.parse_keyword(keyword) # 獲取了頁面
def parse_keyword(self, keyword):
# 解析登陸以後的頁面,篩選出包含這個關鍵字的python代碼
user_repositorys = set() # 集合用來存放作者名和庫名
try:
for i in range(101):
url = "https://github.com/search?l=Python&p={id}&q={keyword}&type=Code".format(id=i+1, keyword=keyword) # 循環的爬取頁面信息
resp = self.session.get(url=url, headers=self.login_headers, verify=False)
soup = BeautifulSoup(resp.text, "lxml")
pattern = soup.find_all('a', class_='text-bold')
for item in pattern:
user_repositorys.add(item.string)
for user_repository in user_repositorys:
self.get_results(user_repository, keyword)
except Exception as e:
print(e)
def get_results(self, repository, keyword): # 用Github的官方API爬取數據,解析json
url = "https://api.github.com/search/code?q={keyword}+in:file+language:python+repo:{w}".format(w=repository, keyword=keyword)
try:
req = Request(url, headers=self.headers)
response = urlopen(req).read()
results = json.loads(response.decode())
for item in results['items']:
repo_url = item["repository"]["html_url"]
file_path = item['html_url']
fork = item["repository"]["fork"]
self.loader_csv(repo_url, file_path, fork, keyword)
except Exception as e:
print("獲取失敗")
def loader_csv(self, repo_url, file_path, keyword):
try:
with open("path", "a") as csv_file:
writer = csv.writer(csv_file)
writer.writerow([keyword, repo_url, file_path])
csv_file.close()
except Exception as e:
print(e)
if __name__ == "__main__":
x = github_crawl()
x.login("user", "password","keyword")