import bs4 as bs
import requests
class finviz_crawler():
def requesting(self,type):
items=1
baseurl='https://finviz.com/screener.ashx?v=111'
if type == 'sp500':
firstpage=baseurl+'&f=idx_sp500&o=ticker'
elif type == "China":
firstpage=baseurl+'&f=cap_largeover,geo_china'
finalurl=firstpage
headers = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
resp=requests.get(finalurl,headers=headers)
soup = bs.BeautifulSoup(resp.text,"lxml")
table=soup.findAll('td',{'class':'screener-body-table-nw'})
maxpage_a_tag=[]
maxpage_td=soup.findAll('td',{'class':'body-table'},bgcolor="#ffffff",valign="top",width="100%")
tickerdict={}
for tags in maxpage_td[0].findAll('a'):
maxpage_a_tag.append(tags)
if not maxpage_a_tag:
maxpage=1
elif (maxpage_a_tag[-1].text=="next"):
maxpage=maxpage_a_tag[-2].text
else:
maxpage=maxpage_a_tag[-1].text
for page in range(int(maxpage)):
resp=requests.get(finalurl,headers=headers)
soup = bs.BeautifulSoup(resp.text,"lxml")
table=soup.findAll('td',{'class':'screener-body-table-nw'})
for row in table:
ticker_rows=row.findAll('a',{'class':'screener-link-primary'},href=True)
for tickers in ticker_rows:
tickerdict[tickers.text]=tickers['href']
items+=20
finalurl="{}{}{}".format(firstpage,"&r=",str(items))
print(tickerdict)
使用
finviz_crawler().requesting('China')
调用类#1 楼
以下是一些改进代码的建议(不分先后顺序):使变量具有局部性
该行:
items = 1
位于方法的顶部,但仅由方法底部的循环使用。
同样适用于:
tickerdict = {}
可以跨多个字符串中断行:
此:
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
可以是:
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/56.0.2924.87 Safari/537.36'
}
您通常不需要循环:
for tags in maxpage_td[0].findAll('a'):
maxpage_a_tag.append(tags)
可能工作得很好,例如:
maxpage_a_tag.extend(maxpage_td[0].findAll('a'))
然后这个:
maxpage_a_tag = []
maxpage_a_tag.extend(maxpage_td[0].findAll('a'))
真的很:
maxpage_a_tag = list(maxpage_td[0].findAll('a'))
不要使用python名称
Python使用
type
这个名字。建议改成type_
或更好,就像注释中所建议的那样,是更具描述性的内容,例如request_type
。可能还有其他一些潜入其中的人。class finviz_crawler():
def requesting(self, type_):
baseurl = 'https://finviz.com/screener.ashx?v=111'
if type_ == 'sp500':
firstpage = baseurl + '&f=idx_sp500&o=ticker'
elif type_ == "China":
firstpage = baseurl + '&f=cap_largeover,geo_china'
else:
raise ValueError("Unknown type {}".format(type_))
finalurl = firstpage
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/56.0.2924.87 Safari/537.36'}
resp = requests.get(finalurl, headers=headers)
soup = bs.BeautifulSoup(resp.text, "lxml")
maxpage_td = soup.findAll('td', {'class': 'body-table'},
bgcolor="#ffffff", valign="top",
width="100%")
maxpage_a_tag = list(maxpage_td[0].findAll('a'))
if not maxpage_a_tag:
maxpage = 1
elif (maxpage_a_tag[-1].text == "next"):
maxpage = maxpage_a_tag[-2].text
else:
maxpage = maxpage_a_tag[-1].text
items = 1
tickerdict = {}
for page in range(int(maxpage)):
resp = requests.get(finalurl, headers=headers)
soup = bs.BeautifulSoup(resp.text, "lxml")
table = soup.findAll('td', {'class': 'screener-body-table-nw'})
find_all_args = 'a', {'class': 'screener-link-primary'}
for row in table:
tickerdict.update(
{tickers.text: tickers['href']
for tickers in row.findAll(*find_all_args, href=True)})
items += 20
finalurl = "{}{}{}".format(firstpage, "&r=", str(items))
for data in tickerdict.items():
print(data)
finviz_crawler().requesting('China')
#2 楼
除了@StephenRauch在其出色的回答中写的内容外,让我再添加一条评论:您的课程完全没有用。不是它的功能很好,但是完全不需要
class
。 它不从父级继承任何东西,也没有从其继承的子级,它没有属性,因此也没有状态,它拥有的一种方法甚至在任何地方都没有使用
self
。它不使用OOP的任何功能。换句话说,您也可以使该方法成为独立函数。而当您使用它时,也许可以通过传递baseurl
作为参数来使其更可重用。一个有价值的相关视频:停止编写类
也,请不要手工构建您的URL,请使用
params
的requests.get
关键字:type_params = {'sp500': {'f': 'idx_sp500', 'o': 'ticker'},
'China': {'f': 'cap_largeover,geo_china'}}
url = 'https://finviz.com/screener.ashx'
params = {'v': 111}
params.update(type_params[type])
headers = {'User-Agent': ...}
resp = requests.get(url, headers=headers, params=params)
必要时,它甚至可以正确引用这些值。
评论
\ $ \ begingroup \ $
是的,是的,我才意识到,非常感谢!!!
\ $ \ endgroup \ $
–长途前进
18年2月19日在18:57
#3 楼
您还应该将代码拆分为功能部分,不要将IO(print(...)
)与生成结果混合在一起。base_url
首先生成base_url
def make_base_url(request_type):
baseurl='https://finviz.com/screener.ashx?v=111'
if request_type == 'sp500':
firstpage=baseurl+'&f=idx_sp500&o=ticker'
elif request_type == "China":
firstpage=baseurl+'&f=cap_largeover,geo_china'
# What will happen when request_type is not `sp500` or `China`
return firstpage
输入不是
sp500
或China
的行为未定义时,这将引发异常get_soup
HEADERS_DEFAULT = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
def get_soup(url, headers=HEADERS_DEFAULT):
resp=requests.get(url,headers=headers)
return bs.BeautifulSoup(resp.text,"lxml")
/>
出现网址时,它会得到汤料。
找到max_page
def find_max_page(base_soup):
td_s = base_soup.findAll('td',{'class':'body-table'},bgcolor="#ffffff",valign="top",width="100%")
a_s = td_s[0].findAll('a')
if not a_s:
# print('found nothing')
return 1
max_page = a_s.pop().text
return int(max_page) if max_page != 'next' else int(a_s.pop().text)
进行
int
转换,其中最合乎逻辑的是查找下一页的网址
def find_page_urls(base_url, max_pages):
yield base_url
for i in range(max_pages):
items = str(1 + (i + 1) * 20)
yield "{}&r={}".format(base_url, items)
提取行情记录
def get_ticker(url, headers=HEADERS_DEFAULT):
soup = get_soup(url, headers)
table=soup.findAll('td',{'class':'screener-body-table-nw'})
for row in table:
ticker_rows=row.findAll('a',{'class':'screener-link-primary'},href=True)
for tickers in ticker_rows:
yield tickers.text, tickers['href']
获取结果
def find_pages(urls, headers=HEADERS_DEFAULT):
results = dict()
for url in urls:
results.update(get_ticker(url, headers))
return results
将其放在一起
base_url = make_base_url('China')
base_soup = get_soup(base_url)
max_pages = find_max_page(base_soup)
urls = find_page_urls(base_url, max_pages, headers=HEADERS_DEFAULT)
result_dict = find_pages(urls)
如果您将其结构化这样,您可以分别测试每个部分,并在需要时手动调试
评论
\ $ \ begingroup \ $
@Michael好吧,根据PEP8,它应该是request_type,但是该名称确实可以更好地传达变量的含义。
\ $ \ endgroup \ $
–地狱
18年2月19日在9:43
\ $ \ begingroup \ $
maxpage_a_tag.extend(list(maxpage_td [0] .findAll('a')))您真的需要在此处转换为列表吗?
\ $ \ endgroup \ $
– Andrea Lazzarotto
18-2-19在20:17
\ $ \ begingroup \ $
@AndreaLazzarotto,可能不是扩展应该进行正确的迭代。在将整个内容移至一个列表分配的解释中,我倒向了这一行,并且没有删除列表强制转换。谢谢。
\ $ \ endgroup \ $
–斯蒂芬·劳赫(Stephen Rauch)
18年2月19日在20:27