My pretty face [ László Monda's Blog ]
Exploring the cyberspace, one quadrant at a time!

Archive for August, 2010

Overclock.net Mechanical Keyboard Guide Atom Feed

Sunday, August 29th, 2010

I use RSS / atom feeds pretty much all the time to minimize information overload but the Mechanical Keyboard Guide of Overclock.net doesn't make my job any easier because they don't provide any feeds and the thread moves very fast.

I couldn't tolerate this anymore so I've created a webscraper that provides atom feeds for this thread. Parsing HTML into a DOM and executing XPath queries on the DOM is something that I have a vast amount experience with and this project didn't take a long time either. I've been testing it for more than a month and it's rock solid. The only glitch is that sometimes posts are randomized between very short time intervals which is a minor inconvenience.

The script below is executed on a hourly basis by cron and its content is saved to http://monda.hu/overclock-net-mech-keyboard.xml

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
< ?php
 
include 'config.php';  // include $database_{servername, username, password, dbname}
 
$start_url = 'http://www.overclock.net/computer-peripherals/491752-mechanical-keyboard-guide-10000.html';
 
function DOMinnerHTML($element)
{
    // Borrowed from php.net
    $innerHTML = "";
    $children = $element->childNodes;
    foreach ($children as $child) {
        $tmp_dom = new DOMDocument();
        $tmp_dom->appendChild($tmp_dom->importNode($child, true));
        $innerHTML .= trim($tmp_dom->saveHTML()) . ' ';
    }
    return $innerHTML;
}
 
function page_to_entries($html)
{
    $domdocument = new DOMDocument();
    @$domdocument->loadHTML($html);
    $domxpath = new DomXPath($domdocument);
    $xpath = '/html/body/div[2]/div/div/div/div/div/table';
    $nodelist = $domxpath->query($xpath);
    $entries = array();
    foreach ($nodelist as $node) {
        $link_node = $node->firstChild->childNodes->Item(2)->childNodes->Item(1);
        $id = $link_node->textContent;
        $url = $link_node->getAttribute('href');
        $username = $node->childNodes->Item(1)->firstChild->childNodes->Item(1)->textContent;
        $comment = DOMInnerHTML($node->childNodes->Item(1)->childNodes->Item(2)->childNodes->Item(8));
        $entry = array('id'=>$id, 'url'=>$url, 'username'=>$username, 'comment'=>$comment);
        $entries[] = $entry;
    }
    return $entries;
}
 
 
function query($sql)
{
    if (($result=mysql_query($sql)) === false) {
        die(mysql_error());
    }
    return $result;
}
 
function register_entry_and_get_timestamp($id)
{
    if (!is_numeric($id)) {
        die("Entry ID is not numeric!");
    }
    $result = query("INSERT IGNORE INTO mechanical_keyboard_guide SET id=$id");
    $result = query("SELECT timestamp FROM mechanical_keyboard_guide WHERE id=$id");
    $row = mysql_fetch_assoc($result);
    $timestamp = strtr($row['timestamp'], ' ', 'T') . 'Z';
    return $timestamp;
}
 
function add_updated_timestamp(&$entries)
{
    for ($i=0; $i<count ($entries); $i++) {
        $entries[$i]['updated'] = register_entry_and_get_timestamp($entries[$i]['id']);
    }
}
 
function print_entry($entry)
{
    $url = $entry['url'];
    $username = htmlspecialchars($entry['username']);
    $comment = $entry['comment'];
    $updated = $entry['updated'];
 
    print "<entry>\n";
    print "     <title>$username</title>\n";
    print "     <link href=\"$url\"/>\n";
    print "     <id>$url</id>\n";
    print "     <updated>$updated</updated>\n";
    print "     <summary type=\"html\">< ![CDATA[$comment]]></summary>\n";
    print "\n";
}
 
// Set up MySQL connection.
if (mysql_connect($database_servername, $database_username, $database_password) === false) {
    die('Failed to connect to the MySQL server.  Please check the $database_servername, $database_username, $database_password variables in config.php');
}
if (mysql_select_db($database_dbname) === false) {
    die('Failed to select the MySQL database.  Please check the $database_dbname variable in config.php');
}
 
// Set up cURL.
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
 
// Fetch last page.
curl_setopt($ch, CURLOPT_URL, $start_url);
$last_page_html = curl_exec($ch);
 
// Get the page ID of the page before the last page.
$last_page_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
preg_match('/-([0-9]+)\.html$/', $last_page_url, $matches);
$last_page_id = $matches[1];
$almost_last_page_id = $last_page_id - 1;
 
// Fetch the page before the last page.
$almost_last_page_url = "http://www.overclock.net/computer-peripherals/491752-mechanical-keyboard-guide-$almost_last_page_id.html";
curl_setopt($ch, CURLOPT_URL, $almost_last_page_url);
$almost_last_page_html = curl_exec($ch);
 
$almost_last_page_entries = page_to_entries($almost_last_page_html);
$last_page_entries = page_to_entries($last_page_html);
$entries = array_merge($almost_last_page_entries, $last_page_entries);
add_updated_timestamp($entries);
$last_updated_timestamp = $entries[count($entries)-1]['updated'];
 
 
print '< ?xml version="1.0" encoding="utf-8" ?>';
?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Overclock.net's Mechanical Keyboards Guide</title>
<subtitle>Overclock.net's Mechanical Keyboards Guide</subtitle>
<link href="http://www.overclock.net/computer-peripherals/491752-mechanical-keyboard-guide.html"/>
<updated>< ?php print $last_updated_timestamp ?></updated>
<author>
<name>Overclock.net's Mechanical Keyboards Guide</name>
<email>laci_nospam@monda.hu</email>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
< ?php foreach ($entries as $entry) print_entry($entry) ?>
</feed>
</count>

As for the SQL table structure, it's not particularly complex.

0
1
2
3
4
CREATE TABLE IF NOT EXISTS `mechanical_keyboard_guide` (
  `id` INT(11) NOT NULL,
  `timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Acer Aspire 8935G-874G100BN laptop disassembly

Sunday, August 29th, 2010

Update (2010-04-22): I've also disassembled the screen.

I've disassembled my laptop a while ago. The CPU core temperature was pretty high, sometimes above 85 celsius. After disassembling and dusting it the temperature dropped by 20 celsius so the operation was a huge success. High temperature has many side effects like decreased durability, increased power consumption, lower performance and who knows what else.

If you decide to disassemble your laptop then I can promise you few things. First, you'll be challenged as laptops are very highly integrated and if you're coming from the PC world you'll quickly realize that it's a completely different ballgame. Second, it's pretty likely that you'll appreciate your laptop more as you'll be able to see the vast scale of integration and all its components.

As for me, I'm absolutely fascinated by the internal design of laptops. I think these gadgets truly symbolize the level of technological advancedment that humanity has reached so far.

I originally wanted to detail the whole disassembly process but I've realized that it'd take too much of my time. Despite of this I hope that you'll enjoy the show. Let's get some popcorn and let the ultimate geek porn begin.

<object width="500" height="375"><param name="flashvars" value="offsite=true&#038;lang=en-us&#038;page_show_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624704112661%2Fshow%2F&#038;page_show_back_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624704112661%2F&#038;set_id=72157624704112661&#038;jump_to="></param><param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=71649"></param><param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=71649" allowFullScreen="true" flashvars="offsite=true&#038;lang=en-us&#038;page_show_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624704112661%2Fshow%2F&#038;page_show_back_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624704112661%2F&#038;set_id=72157624704112661&#038;jump_to=" width="500" height="375"></embed></object>

Let's see some video of the actual dusting.

<object width="640" height="390"><param name="movie" value="http://www.youtube.com/v/EdifKpx9VFs?fs=1&#038;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/EdifKpx9VFs?fs=1&#038;hl=en_US" type="application/x-shockwave-flash" width="640" height="390" allowscriptaccess="always" allowfullscreen="true"></embed></object>

G-Cube GUA-54A USB hub disassembly

Sunday, August 29th, 2010

This hub uses the most popular USB 2.0 hub IC, the GL850G. I'm not sure whether the manufacturer used ultrasonic welding or glue, but this hub cannot be disassembled without significantly damaging the case and I pretty much hate such solutions. USB hubs pose a perfect example of how much additional value an ODM can provide on top of an OEM.

<object width="500" height="375"><param name="flashvars" value="offsite=true&#038;lang=en-us&#038;page_show_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624703872265%2Fshow%2F&#038;page_show_back_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624703872265%2F&#038;set_id=72157624703872265&#038;jump_to="></param><param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=71649"></param><param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=71649" allowFullScreen="true" flashvars="offsite=true&#038;lang=en-us&#038;page_show_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624703872265%2Fshow%2F&#038;page_show_back_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624703872265%2F&#038;set_id=72157624703872265&#038;jump_to=" width="500" height="375"></embed></object>

Noname card reader disassembly

Sunday, August 29th, 2010

It's interesting how one chip handles 4 types of cards. I wonder whether the various card specs are that similar or whether the IC is that highly integrated.

<object width="500" height="375"><param name="flashvars" value="offsite=true&#038;lang=en-us&#038;page_show_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624684139923%2Fshow%2F&#038;page_show_back_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624684139923%2F&#038;set_id=72157624684139923&#038;jump_to="></param><param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=71649"></param><param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=71649" allowFullScreen="true" flashvars="offsite=true&#038;lang=en-us&#038;page_show_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624684139923%2Fshow%2F&#038;page_show_back_url=%2Fphotos%2Fmondalaci%2Fsets%2F72157624684139923%2F&#038;set_id=72157624684139923&#038;jump_to=" width="500" height="375"></embed></object>