1
|
1 #!/usr/bin/env python
|
|
2
|
|
3 ############################################################################
|
|
4 # Screen scraper for game-monitor.com
|
|
5 #
|
|
6 # Prints out matched player names agreated by server
|
|
7 #
|
|
8 ############################################################################
|
|
9 #
|
15
|
10 # Copyright (C) 2008 Daniel O'Connor. All rights reserved.
|
1
|
11 #
|
|
12 # Redistribution and use in source and binary forms, with or without
|
|
13 # modification, are permitted provided that the following conditions
|
|
14 # are met:
|
|
15 # 1. Redistributions of source code must retain the above copyright
|
|
16 # notice, this list of conditions and the following disclaimer.
|
|
17 # 2. Redistributions in binary form must reproduce the above copyright
|
|
18 # notice, this list of conditions and the following disclaimer in the
|
|
19 # documentation and/or other materials provided with the distribution.
|
|
20 #
|
|
21 # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
22 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
23 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
24 # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
25 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
26 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
27 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
28 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
30 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
31 # SUCH DAMAGE.
|
|
32 #
|
|
33 ############################################################################
|
|
34
|
|
35 import re, time, datetime, urllib, sys, BeautifulSoup
|
|
36
|
15
|
37 debug = False
|
|
38
|
1
|
39 class Server:
|
|
40 alltags = re.compile('<[^>]*>')
|
|
41 vwhttags = re.compile('<(br|hr)>')
|
|
42 hwhttags = re.compile('\ ')
|
15
|
43 typetag = re.compile('<td><a href="/GameSearch/([^/]+)/.*</td>')
|
|
44
|
1
|
45 def __init__(self, description = "", ip = "", port = 0, mapname = "",
|
|
46 updateage = 0, numplayers = 0, maxplayers = 0, players = []):
|
|
47 self.description = description
|
|
48 self.ip = ip
|
|
49 self.port = port
|
|
50 self.mapname = mapname
|
|
51 self.updateage = int(updateage)
|
|
52 self.players = []
|
|
53 self.numplayers = numplayers
|
|
54 self.maxplayers = maxplayers
|
|
55
|
|
56 def __init__(self, pcols, scols):
|
15
|
57 # pcols[1] = Player name
|
|
58 # pcols[2] = Server description
|
1
|
59 # scols[0] = Players in server / max players
|
15
|
60 # scols[1] = Server IP & port
|
|
61 # scols[2] = Map name
|
|
62 # scols[3] = Game type
|
|
63 # scols[8] = Update age
|
|
64 if debug:
|
|
65 print "pcols = " + str(pcols)
|
|
66 print "scols = " + str(scols)
|
|
67
|
|
68 self.pcountre = re.compile("([0-9]+)/([0-9]+)")
|
|
69 self.ipportre = re.compile("([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):([0-9]+)")
|
|
70 self.sdesc = re.compile(" +[0-9]+\. +(.*)")
|
|
71
|
|
72 m = self.sdesc.match(pcols[2])
|
|
73 if (m == None):
|
|
74 raise SyntaxError
|
|
75 self.description = m.group(1)
|
|
76
|
|
77 m = self.ipportre.match(scols[1])
|
|
78 if (m == None):
|
|
79 raise SyntaxError
|
|
80
|
|
81 self.ip = m.group(1)
|
|
82 self.port = int(m.group(2))
|
|
83 self.gametype = scols[3]
|
|
84 self.mapname = scols[2]
|
|
85 self.updateage = scols[8]
|
|
86 m = self.pcountre.match(scols[0])
|
1
|
87 if (m == None):
|
|
88 raise SyntaxError
|
|
89
|
|
90 self.numplayers = int(m.group(1))
|
|
91 self.maxplayers = int(m.group(2))
|
|
92 self.players = []
|
|
93
|
|
94 def __str__(self):
|
|
95 plist = ""
|
|
96 for p in self.players:
|
|
97 plist = plist + " " + str(p)
|
|
98
|
15
|
99 return "%s: %s (%s:%d) | Map: %s | Players: %d/%d : %s (%s old)" % \
|
|
100 (self.gametype, self.description, self.ip, self.port, self.mapname,
|
|
101 self.numplayers, self.maxplayers, plist,
|
|
102 self.updateage)
|
1
|
103
|
|
104 def GetTuple(scols):
|
|
105 return str(scols[2]) + ":" + str(scols[3])
|
|
106 GetTuple = staticmethod(GetTuple)
|
|
107
|
|
108 def FixTags(s):
|
15
|
109 # Mangle game type
|
|
110 t = Server.typetag.match(s)
|
|
111 if t != None:
|
|
112 s = t.group(1)
|
1
|
113 s = re.sub(Server.vwhttags, '\n', s)
|
|
114 s = re.sub(Server.hwhttags, '', s)
|
|
115 s = str(BeautifulSoup.BeautifulStoneSoup( \
|
|
116 s, convertEntities = BeautifulSoup.BeautifulStoneSoup.XML_ENTITIES))
|
|
117 s = re.sub(Server.alltags, '', s)
|
|
118 return(s)
|
|
119 FixTags = staticmethod(FixTags)
|
|
120
|
|
121 def Scrape(handle):
|
|
122 s = BeautifulSoup.BeautifulSoup(handle)
|
|
123
|
15
|
124 playertbl = s.find("table", "results")
|
1
|
125 if (playertbl == None):
|
15
|
126 if True:
|
|
127 print "Unable to find results"
|
1
|
128 return None
|
|
129
|
15
|
130 servertbl = playertbl.findNext("table")
|
1
|
131
|
|
132 playerrows = playertbl.findAll("tr")
|
|
133 serverrows = servertbl.findAll("tr")
|
|
134
|
|
135 if (len(playerrows) != len(serverrows)):
|
|
136 print "Internal error 41223"
|
|
137 return
|
|
138
|
|
139 servers = {}
|
|
140 for i in range(len(playerrows[1:])):
|
|
141 pcols = playerrows[i].findAll('td')
|
|
142 scols = serverrows[i].findAll('td')
|
15
|
143 if (len(pcols) != 3):
|
|
144 if debug:
|
|
145 print "pcols has length %d, expected 3" % len(pcols)
|
1
|
146 continue
|
|
147
|
|
148 pcols = map(lambda c : Server.FixTags(str(c)), pcols)
|
|
149 scols = map(lambda c : Server.FixTags(str(c)), scols)
|
|
150
|
|
151 stuple = Server.GetTuple(scols)
|
|
152
|
|
153 if (stuple not in servers):
|
|
154 s = Server(pcols, scols)
|
|
155 servers[stuple] = s
|
|
156
|
15
|
157 servers[stuple].addplayer(pcols[1])
|
1
|
158
|
|
159 return servers
|
|
160 Scrape = staticmethod(Scrape)
|
|
161
|
|
162 def addplayer(self, pname):
|
|
163 self.players.append(pname)
|
|
164
|
|
165
|
15
|
166 if True:
|
1
|
167 maxhits = 10
|
|
168 if (len(sys.argv) < 2):
|
|
169 print "Bad usage"
|
|
170 print sys.argv[0] + "search_string"
|
|
171 sys.exit(1)
|
|
172
|
|
173 try:
|
|
174 #f = open("gm.html")
|
15
|
175 f = urllib.urlopen("http://www.game-monitor.com/search.php?location=AU&search=" + urllib.quote(sys.argv[1]) + "&type=player&location=AU")
|
1
|
176 except IOError, e:
|
|
177 print "Unable to fetch page - " + str(e)
|
|
178 sys.exit(0)
|
|
179
|
|
180 servers = Server.Scrape(f)
|
|
181 del f
|
6
|
182 if (servers == None):
|
|
183 print "No results available, please check manually"
|
|
184 elif (len(servers) == 0):
|
1
|
185 print "No players found"
|
|
186 else:
|
15
|
187 tmp = []
|
|
188 for i in servers:
|
|
189 tmp.append(servers[i])
|
|
190 tmp.sort()
|
1
|
191 i = 0
|
15
|
192 for s in tmp:
|
1
|
193 i = i + 1
|
15
|
194 print s
|
1
|
195 if (i >= maxhits):
|
|
196 print "*** Stopping after " + str(maxhits) + " hits"
|
|
197 break
|